在 Java 中使用 WSS4j 对肥皂消息进行签名

2024-02-02

我正在编写一个 Java 客户端应用程序,需要签署 SOAP 消息(其中包含 2 个部分)并将其发送到远程服务器,然后再获取响应。我成功地在 SoapUI 中进行了调用(请参阅下面的请求和屏幕截图)。

我已准备好大部分代码,但我不断从服务器收到一条错误消息:

SECU3504: Digital signature verification failure.
Signature failed core validation

Signature validation status: true
ref[#id-32e3db92-b6fd-42a5-b032-a0dc2a15ae82] validity status: false
ref[#id-2b67ce75-e25f-4f66-b265-80a0e31911ec] validity status: false 

这是我的Java代码:

// Load KeyStore from .PFX certificate
KeyStore store = KeyStore.getInstance("PKCS12");
store.load(new FileInputStream(certificateFileName), certificatePassword.toCharArray());

// Build XML Document from String
String data = "..."; // Same request as in SoapUI
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
doc = dBuilder.parse(new ByteArrayInputStream(data.getBytes()));

// Load certificate
String alias = store.aliases().nextElement(); // There's only one
PrivateKeyEntry keyEntry = (PrivateKeyEntry)store.getEntry(alias, new KeyStore.PasswordProtection(password.toCharArray()));
X509Certificate cert = (X509Certificate) keyEntry.getCertificate();

// Add <wsse:Security> tag.
WSSecHeader secHeader = new WSSecHeader();
secHeader.setMustUnderstand(false);
secHeader.insertSecurityHeader(doc);

// It is not added to the right place, so move it under the right header       
Node n = doc.getFirstChild().getFirstChild().getFirstChild();           
Element elemEnvelope = (Element)doc.getElementsByTagName("soapenv:Envelope").item(0);
Element elemNewHeader = (Element)doc.getElementsByTagName("Header").item(0);
Element elemSoapHeader = (Element)doc.getElementsByTagName("soapenv:Header").item(0);           
Element elemBody = (Element)doc.getElementsByTagName("soapenv:Body").item(0);
Element elemHeader = (Element)doc.getElementsByTagName("ns1:CAISOWSHeader").item(0);
elemSoapHeader.insertBefore(n, elemHeader);
elemEnvelope.removeChild(elemNewHeader);

// Setup signing algorithm
WSSecSignature builder = new WSSecSignature();
builder.setX509Certificate(cert);
builder.setUserInfo(alias, password);
builder.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
builder.setSignatureAlgorithm(WSConstants.RSA_SHA1);
builder.setSigCanonicalization(WSConstants.C14N_EXCL_OMIT_COMMENTS);            
builder.setDigestAlgo(WSConstants.SHA1);
builder.setUseSingleCertificate(true);

// Set message parts to sign
List<WSEncryptionPart> parts = new ArrayList<WSEncryptionPart>();
WSEncryptionPart bodyPart1 = new WSEncryptionPart("Body", WSConstants.URI_SOAP11_ENV, "Content");
WSEncryptionPart bodyPart2 = new WSEncryptionPart("CAISOWSHeader", "http://www.caiso.com/soa/2006-09-30/CAISOWSHeader.xsd", "Content");
bodyPart1.setElement(elemBody);
bodyPart2.setElement(elemHeader);
parts.add(bodyPart1);
parts.add(bodyPart2);
builder.getParts().addAll(parts);           

// Set keystore and sign the document
Properties properties = new Properties();
properties.setProperty("org.apache.ws.security.crypto.provider", "org.apache.wss4j.common.crypto.Merlin");

Merlin crypto = (Merlin)CryptoFactory.getInstance(properties);
crypto.setKeyStore(store);

doc = builder.build(doc, crypto, secHeader);
String docStr = this.toString(doc); // This method generates a string of the Document.

// Send SOAP message
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
HttpPost httppost = new HttpPost(this.url);
HttpEntity entity = new ByteArrayEntity(docStr .getBytes("UTF-8"));
httppost.setEntity(entity); // Signed Body
httppost.addHeader("SOAPAction", "http://www.caiso.com/soa/retrieveConvergenceBidAwards_v2");

HttpResponse response = httpclient.execute(httppost);
String respStr = EntityUtils.toString(response.getEntity());

I managed to make successful calls in SoapUI with the following security settings:enter image description here

我在SoapUI中放入的请求如下(Dates/MessageId/Nonce是当场生成的,但为了可读性,我将它们硬编码在这里):

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:stan="http://www.caiso.com/soa/2006-06-13/StandardAttachmentInfor.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <soapenv:Header>
      <ns1:CAISOWSHeader xmlns:ns1="http://www.caiso.com/soa/2006-09-30/CAISOWSHeader.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soapenv:actor="" soapenv:mustUnderstand="0">
         <ns1:CAISOUsernameToken>
            <ns1:Username>CN=XXXX, OU=people, O=XXXX, C=US</ns1:Username>
            <Nonce xmlns:ns2="http://schemas.xmlsoap.org/ws/2002/07/secext" EncodingType="http://docs.oasisopen.org/wss/2004/01/oasis-200401wss-soap-message-security-1.0#Base64Binary">MGNkNmU5ODMtZjcyZC00YTAyLWE5NWMtM2Q5Y2RjMTEyNDA1</Nonce>
            <Created xmlns:ns3="http://schemas.xmlsoap.org/ws/2002/07/utility">2018-03-02T21:13:45.932Z</Created>
         </ns1:CAISOUsernameToken>
         <CAISOMessageInfo>
            <MessageID>06c3379d-f10e-45f2-84eb-9262acce277e</MessageID>
            <Timestamp>
               <Created xmlns:ns4="http://schemas.xmlsoap.org/ws/2002/07/utility">2018-03-02T21:13:45.932Z</Created>
               <Expires xmlns:ns5="http://schemas.xmlsoap.org/ws/2002/07/utility">2018-03-02T23:13:45.932Z</Expires>
            </Timestamp>
         </CAISOMessageInfo>
      </ns1:CAISOWSHeader>
      <attachmentHash xmlns:ns7="http://www.caiso.com/mrtu/soa/schemas/2005/09/attachmenthash" actor="http://schemas.xmlsoap.org/soap/actor/next" mustUnderstand="0">
         <hashValue />
      </attachmentHash>
      <standardAttachmentInfor xmlns:ns1="http://www.caiso.com/soa/2006-06-13/StandardAttachmentInfor.xsd">
         <Attachment>
            <id>1</id>
            <compressFlag>yes</compressFlag>
            <compressMethod>gzip</compressMethod>
         </Attachment>
      </standardAttachmentInfor>
   </soapenv:Header>
   <soapenv:Body>
      <retrieveConvergenceBidAwards_v2>
         <RequestConvergenceBidAwards>
            <MessagePayload>
               <RequestConvergenceBidAwardRecord>
                  <dateTimeEnd>2018-02-02T08:00:00Z</dateTimeEnd>
                  <dateTimeStart>2018-02-01T08:00:00Z</dateTimeStart>
                  <SchedulingCoordinatorList>
                     <schedulingCoordinator>XXXX</schedulingCoordinator>
                  </SchedulingCoordinatorList>
               </RequestConvergenceBidAwardRecord>
            </MessagePayload>
         </RequestConvergenceBidAwards>
      </retrieveConvergenceBidAwards_v2>
   </soapenv:Body>
</soapenv:Envelope>

我不知道问题出在哪里。是在签名过程中、从文档到字符串的转换过程中还是在我执行 HTTP 请求的过程中?

非常感谢帮助:)


我知道这是一个老问题,但我一直在努力解决这个问题,我终于找到了一种生成签名的方法,就像 Soap UI 所做的那样,这是我的代码:

public String handleMessage(SOAPMessage message) {
        String ret;
        try {
            Document doc = message.getSOAPBody().getOwnerDocument();
            Crypto crypto = CryptoFactory.getInstance(properties); //File

            WSSecSignature sign = new WSSecSignature();
            sign.setUserInfo(properties.getProperty("org.apache.ws.security.crypto.merlin.keystore.alias"), properties.getProperty("privatekeypassword"));
            sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE); // Binary Security Token - SecurityTokenReference
            sign.setUseSingleCertificate(true);
            sign.setDigestAlgo(DigestMethod.SHA256);

            WSSecHeader secHeader = new WSSecHeader();
            secHeader.insertSecurityHeader(doc);
            Document signedDoc = sign.build(doc, crypto, secHeader);

            ret = org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(signedDoc);

        } catch (SOAPException e) {
            e.printStackTrace();
            return null;
        } catch (WSSecurityException e) {
            e.printStackTrace();
            throw new RuntimeException("Error: " + e.getMessage());
        }
        return ret;
    }

该代码取自:https://sourceforge.net/p/signsoaprequest/code/HEAD/tree/SignSOAPRequest/trunk/src/main/java/br/gov/dataprev/soaptools/sign/ https://sourceforge.net/p/signsoaprequest/code/HEAD/tree/SignSOAPRequest/trunk/src/main/java/br/gov/dataprev/soaptools/sign/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Java 中使用 WSS4j 对肥皂消息进行签名 的相关文章

  • 在 Java 中实现排列算法的技巧

    作为学校项目的一部分 我需要编写一个函数 该函数将接受整数 N 并返回数组 0 1 N 1 的每个排列的二维数组 该声明看起来像 public static int permutations int N 该算法描述于http www usn
  • 使用 Java 的 OpenId 提供者/服务器

    我正在尝试使用 OpenId 服务增强现有的 Java Web 应用程序 以便登录用户可以使用我的 Web 应用程序作为 OpenId 提供程序登录另一个启用 OpenId 的应用程序 My first attempt was to use
  • 杰克逊.将缺失的属性反序列化为空Optional

    假设我有一堂这样的课 public static class Test private Optional
  • 如何找出已使用的 JAR?

    在更大的项目中 我们可能会使用大量的 JAR 如何找出项目 而不是整个项目 中的某个模块 包正在使用哪些 JAR 有什么工具 技术等吗 较大的项目通常使用类似的构建工具maven http maven apache org or ant h
  • 可以显式删除 lambda 的序列化支持

    As 已经知道 https stackoverflow com a 22808112 2711488很容易添加序列化当目标接口尚未继承时支持 lambda 表达式Serializable 就像 TargetInterface Seriali
  • 如何使 java.text.NumberFormat 将 0.0d 格式设置为“0”而不是“+0”?

    需要带符号的结果 0 0d 除外 IE 123 45d gt 123 45 123 45d gt 123 45 0 0d gt 0 我调用format setPositivePrefix 在 DecimalFormat 的实例上 强制结果中
  • 如何在 Java 中用 \n 替换 \\n

    我有一个string test first n middle n last 现在我想更换所有 n by n 我试过了test replaceAll n n and test replaceAll n n 但它们不起作用 有人有解决办法吗 T
  • Spring的@PreDestroy导致随机记录而不记录

    我正在使用 Spring 并且在终止时我让 PreDestroy 清理 bean 我不明白为什么日志记录有时会成功 而有时会失败 Using Log4j2 Logger log LogManager getLogger MyClass cl
  • kafka消费端Offsets的一致性

    我有复制因子为 3 的卡夫卡主题min insync replicas 2 一个向该主题发送 X 条消息的生产者acks all 一段时间后 1 分钟内 在所有消息发送到主题后 将使用 java kafka 客户端为此主题创建新的消费者 使
  • 在 JUnit 测试中读取资源文件

    我在单元测试中读取文本文件 并将一些输入文本文件放置在资源文件夹中 以下是目录结构 src gt com gt au gt myapp gt util gt MyFileReader 测试 gt com gt au gt myapp gt
  • 如何自定义 JFrame 上的标题栏?

    我想在我的 Java Swing 桌面应用程序中拥有一个自定义的标题栏 最好的方法是什么 我可以通过在 JFrame 的构造函数中使用以下代码来使用 Swing 标题栏 this setUndecorated true this getRo
  • Android 改造参数化@Headers

    我正在使用 OAuth 每次发出请求时都需要将 OAuth 令牌放入标头中 我看到 Header注释 但是有没有办法让它参数化 以便我可以在运行时传入 这是概念 Header Authorization OAuth var api vers
  • 具有多个注释的方法上的 AspectJ 切入点

    使用加载时编织 纯 AspectJ 我们有2个注释 Time and Count 以及一些带注释的方法 Time name myMethod1Time Count name myMethod1Count public void myMeth
  • 将 try catch finally 块放入另一个 finally 块中

    try catch finally try catch finally 上面的代码好不好 是的 你可以这样做 实际上 在处理想要正确关闭的流时 您甚至需要这样做 InputStream in try catch finally try in
  • Android:如何停止监听电话监听器? [复制]

    这个问题在这里已经有答案了 可能的重复 Android 为什么 PhoneCallListener 在活动完成后仍然存在 https stackoverflow com questions 11666853 android why phon
  • 为什么我们在同一台服务器上使用多个应用程序服务器实例

    我想这是有充分理由的 但我不明白为什么有时我们会在同一物理服务器上放置例如 5 个具有相同 Web 应用程序的实例 这与多处理器架构的优化有关吗 JVM 或其他允许的最大内存限制 嗯 过了很长一段时间我又看到这个问题了 一台机器上的多个 J
  • Guava MultiSet 与 Map?

    我对Multiset的理解是一个带有频率的集合 但是我总是可以使用Map来表示频率 还有其他原因使用Multiset吗 优点Multiset
  • Java并发锁和条件的使用

    我可以用object wait object notify and synchronized blocks解决生产者消费者类型的问题 同时我可以使用locks and conditions from java util concurrent
  • 为什么ArrayList的非静态内部类SubList有一个成员变量“parent”?

    java util ArrayList SubList 是 java util ArrayList 的非静态内部类 这意味着它保存对其封闭类的引用 我们可以使用ArrayList this来访问java util ArrayList的成员
  • POJO 支持使用omnifaces 自动完成primefaces

    我正在尝试在我的项目中使用 primefaces 自动完成组件 以避免将特定转换器写入我尝试使用的每个列表对象全能面孔 http showcase omnifaces org converters ListConverter如建议的here

随机推荐