首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何创建用于Web服务的密码摘要?

如何创建用于Web服务的密码摘要?
EN

Stack Overflow用户
提问于 2014-05-29 02:54:53
回答 3查看 25.5K关注 0票数 6

我正在尝试创建一个passwordDigest util,它可以在不同的环境中使用,这些环境能够运行java字节码。

首先,我创建了nonce。它是这样做的。

代码语言:javascript
复制
public static String buildNonce(){
        StringBuffer nonce=new StringBuffer();
        String dateTimeString = Long.toString(new Date().getTime());
        byte[] nonceByte= dateTimeString.getBytes();
        return Base64.encode(nonceByte);
    }

一旦我有了nonce,我就会构建密码摘要。

代码语言:javascript
复制
public static String buildPasswordDigest(String userName, String password, String nonce, String dateTime){
    MessageDigest sha1;
    String passwordDigest=null;
    try {
        sha1= MessageDigest.getInstance("SHA-1");
        byte[] hash = MessageDigest.getInstance("SHA-1").digest(password.getBytes("UTF-8"));
        sha1.update(nonce.getBytes("UTF-8"));
        sha1.update(dateTime.getBytes("UTF-8"));
        passwordDigest = new String(Base64.encode(sha1.digest(hash)));
        sha1.reset();
    } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return passwordDigest;

以测试一切是否正常工作。我已经使用CXF2.7创建了一个测试web服务。我已经手动创建了SOAP信封来测试身份验证。信封看起来像这样。

代码语言:javascript
复制
 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
                xmlns:ws="http://ws.mytest.org/" 
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance/">
    <soapenv:Header>
        <wsse:Security soapenv:mustUnderstand="1" 
          xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" 
          xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
          <wsse:UsernameToken wsu:Id="UsernameToken-2">
            <wsse:Username>TEST_USER</wsse:Username>
            <wsse:Password 
            Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">UZsDSW/vANu6fHg4rAHo2OwsF9s=</wsse:Password
            <wsse:Nonce>MTQwMTMwMDQzNjA3OA==</wsse:Nonce>
            <wsu:Created>2014-05-28T18:07:16.087Z</wsu:Created>
         </wsse:UsernameToken>
        </wsse:Security>
    </soapenv:Header>
    <soapenv:Body>
        <ws:record>
            <val1>1</val1>
            <val2>Some Text</val2>
        </ws:record>
    </soapenv:Body>
</soapenv:Envelope>

当我使用SOAP UI发送信封时。我得到以下身份验证错误。

代码语言:javascript
复制
WARNING: Interceptor for {http://ws.mytest.org/}TestService has thrown exception, unwinding now
org.apache.cxf.binding.soap.SoapFault: The security token could not be authenticated or authorized
    at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.createSoapFault(WSS4JInInterceptor.java:788)
    at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:336)
    at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:95)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:272)
    at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
    at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:239)
    at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:248)
    at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:222)
    at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:153)
    at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:167)
    at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:286)
    at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:206)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
    at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:262)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:313)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)
Caused by: org.apache.ws.security.WSSecurityException: The security token could not be authenticated or authorized
    at org.apache.ws.security.validate.UsernameTokenValidator.verifyDigestPassword(UsernameTokenValidator.java:199)
    at org.apache.ws.security.validate.UsernameTokenValidator.validate(UsernameTokenValidator.java:97)
    at org.apache.ws.security.processor.UsernameTokenProcessor.handleUsernameToken(UsernameTokenProcessor.java:172)
    at org.apache.ws.security.processor.UsernameTokenProcessor.handleToken(UsernameTokenProcessor.java:67)
    at org.apache.ws.security.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:396)
    at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:279)
    ... 31 more

我怀疑我在创建nonce或password时遇到了问题。

非常感谢您的帮助。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-01-21 15:32:17

WS-Security将密码摘要定义为

创建密码( SHA1 (

+ created + Base64 ))

Base64 ( SHA1 (密码+随机数+已创建))

并且随机数被认为是编码为Base64的128比特(16字节)。例如:

代码语言:javascript
复制
java.security.SecureRandom random = java.security.SecureRandom.getInstance("SHA1PRNG");
random.setSeed(System.currentTimeMillis()); 
byte[] nonceBytes = new byte[16]; 
random.nextBytes(nonceBytes); 
String nonce = new String(org.apache.commons.codec.binary.Base64.encodeBase64(nonceBytes), "UTF-8");
票数 9
EN

Stack Overflow用户

发布于 2019-12-27 20:55:38

应该构造nonce和密码摘要,就像在下面的代码片段中所做的那样。

请注意元素的顺序,以及用于passwordDigest构造的非编码版本的nonce

代码语言:javascript
复制
import javax.xml.datatype.*;
import java.security.*;
import java.time.Instant;
import java.util.Base64;

import static java.lang.System.currentTimeMillis;
import static java.nio.charset.StandardCharsets.UTF_8;

class Snippet {

    private static final SecureRandom RANDOM;
    private static final int NONCE_SIZE_IN_BYTES = 16;
    private static final String MESSAGE_DIGEST_ALGORITHM_NAME_SHA_1 = "SHA-1";
    private static final String SECURE_RANDOM_ALGORITHM_SHA_1_PRNG = "SHA1PRNG";

    static {
        try {
            RANDOM = SecureRandom.getInstance(SECURE_RANDOM_ALGORITHM_SHA_1_PRNG);
            RANDOM.setSeed(currentTimeMillis());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws DatatypeConfigurationException {
        final var nonceBytes = generateNonce();

        final var password = "password";
        final var createdDate = DatatypeFactory.newInstance().newXMLGregorianCalendar(Instant.now().toString());
        final var passwordDigestBytes = constructPasswordDigest(nonceBytes, createdDate, password);

        final var base64Encoder = Base64.getEncoder();
        final var nonceBase64Encoded = base64Encoder.encodeToString(nonceBytes);
        final var passwordDigestBase64Encoded = base64Encoder.encodeToString(passwordDigestBytes);
        System.out.println(String.format("nonce: [%s], password digest: [%s]", nonceBase64Encoded, passwordDigestBase64Encoded));
        System.out.flush();
    }

    private static byte[] generateNonce() {
        var nonceBytes = new byte[NONCE_SIZE_IN_BYTES];
        RANDOM.nextBytes(nonceBytes);
        return nonceBytes;
    }

    /**
     * @noinspection SameParameterValue
     */
    private static byte[] constructPasswordDigest(byte[] nonceBytes, XMLGregorianCalendar createdDate, String password) {
        try {
            final var sha1MessageDigest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_NAME_SHA_1);
            sha1MessageDigest.update(nonceBytes);
            final var createdDateAsString = createdDate.toString();
            sha1MessageDigest.update(createdDateAsString.getBytes(UTF_8));
            sha1MessageDigest.update(password.getBytes(UTF_8));
            return sha1MessageDigest.digest();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }
}

此代码段的输出为:

随机数: KEyJbsmxL1JdsDHo7kWD6Q==,密码摘要:+OiJDs2sEycZECHhQdJ8T9Lt2ns=

票数 2
EN

Stack Overflow用户

发布于 2021-11-18 06:48:57

经过日复一日的搜索,帕维尔的答案是唯一对我有效的答案。我对他的代码所做的唯一更改是: 1)返回令牌创建的日期,其中包括nonce和password digest。这对于避免“过期令牌”响应至关重要。2)返回整个头块3)将代码转换为Java 1.8。

以下是修改后的Pavel代码。

代码语言:javascript
复制
package test;

import javax.xml.datatype.*;
import java.security.*;
import java.time.Instant;
import java.util.Base64;
import java.util.Base64.Encoder;

import static java.lang.System.currentTimeMillis;
import static java.nio.charset.StandardCharsets.UTF_8;

public class Main {

private static final SecureRandom RANDOM;
private static final int NONCE_SIZE_IN_BYTES = 16;
private static final String MESSAGE_DIGEST_ALGORITHM_NAME_SHA_1 = "SHA-1";
private static final String SECURE_RANDOM_ALGORITHM_SHA_1_PRNG = "SHA1PRNG";

static {
    try {
        RANDOM = SecureRandom.getInstance(SECURE_RANDOM_ALGORITHM_SHA_1_PRNG);
        RANDOM.setSeed(currentTimeMillis());
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException(e);
    }
}

public static void main(String[] args) throws DatatypeConfigurationException {
    
    String passwordMd5 = "4C2d85FBEE80145B4";

    generateHeader(passwordMd5);

}

public static void generateHeader(String password) throws DatatypeConfigurationException {

    final byte[] nonceBytes = generateNonce();

    final XMLGregorianCalendar createdDate = DatatypeFactory.newInstance()
            .newXMLGregorianCalendar(Instant.now().toString());
    final byte[] passwordDigestBytes = constructPasswordDigest(nonceBytes, createdDate, password);

    final Encoder base64Encoder = Base64.getEncoder();
    final String nonceBase64Encoded = base64Encoder.encodeToString(nonceBytes);
    final String passwordDigestBase64Encoded = base64Encoder.encodeToString(passwordDigestBytes);
    
    //System.out.println(String.format("nonce: [%s], password digest: [%s]", nonceBase64Encoded, passwordDigestBase64Encoded));

    StringBuilder sb = new StringBuilder();

    sb.append("<soapenv:Header>\n");
    sb.append("<wsse:Security xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">\n");
    
    sb.append("<wsse:UsernameToken>");
    sb.append("<wsse:Username>thisIsMyUsername</wsse:Username>");
    
    sb.append("<wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest\">"+
    passwordDigestBase64Encoded+"</wsse:Password>\n");
    
    sb.append("<wsse:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004 /01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">" +
    nonceBase64Encoded+"</wsse:Nonce>\n");
    
    sb.append("<wsu:Created xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">" +
    createdDate.toString()+"</wsu:Created>\n");
    
    sb.append("</wsse:UsernameToken>\n");
    sb.append("</wsse:Security>\n");
    sb.append("</soapenv:Header>");

    
    System.out.println(sb.toString());
    
    System.out.flush();
    
    System.exit(0);
}

private static byte[] generateNonce() {
    byte[] nonceBytes = new byte[NONCE_SIZE_IN_BYTES];
    RANDOM.nextBytes(nonceBytes);
    return nonceBytes;
}

private static byte[] constructPasswordDigest(byte[] nonceBytes, XMLGregorianCalendar createdDate,
        String password) {
    try {
        final MessageDigest sha1MessageDigest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_NAME_SHA_1);
        sha1MessageDigest.update(nonceBytes);
        final String createdDateAsString = createdDate.toString();
    

        sha1MessageDigest.update(createdDateAsString.getBytes(UTF_8));
        sha1MessageDigest.update(password.getBytes(UTF_8));
        return sha1MessageDigest.digest();
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException(e);
    }
}

}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23919680

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档