我正在尝试向IRS发送Soap请求并面临与此组中其他人相同的错误 – “无效的WS安全标头”.有人可以用样品肥皂请求指导我吗?还有一个问题是 – 作为注册过程的一部分,我们将我们的X509证书(公钥)提交给IRS网站,该网站将用于验证/解密您的邮件摘要.您为此流程上传了哪个证书文件?我们真的坚持这个错误已经好几天了.感谢任何帮助.我已经看过2个关于这个主题的问题,但没有帮助答案.
解决方法
我假设这是ACA Air IRS提交的.我们将.cer文件上传到IRS站点,在那里您将TCC(例如,BBBBB格式)与您上传的.cer相关联.我们使用的堆栈是:Oracle的JDK 8,WSS4J v2.1.4和CXF v3.1.4.以下是我们用于签署IRS想要签名的参考元素的示例Java代码:
public static SOAPMessage signSoapMessage(SOAPMessage message,String keystorePassword,String irsPrivateKeyPassword,Class<?> clazz) throws WSSecurityException { //TODO remove below hard coded final String _irsPrivateKeyPassword = "yourprivatekeypasswordyougotfromCA"; final String _keystorePassword = "yourpasswordtoyourJKS"; keystorePassword = _keystorePassword; irsPrivateKeyPassword = _irsPrivateKeyPassword; PrivateKeyEntry privateKeyEntry = getPrivateKeyEntry(keystorePassword,irsPrivateKeyPassword); PrivateKey signingKey = privateKeyEntry.getPrivateKey(); X509Certificate signingCert = (X509Certificate) privateKeyEntry .getCertificate(); //TODO add alias to database final String alias = "thealiasforthiscertandprivatekey"; final int signatureValidityTime = 3600; // 1hour in seconds WSSConfig config = WSSConfig.getNewInstance(); //config.setWsiBSPCompliant(true); WsuIdAllocator idAllocator = new WsuIdAllocator() { @Override public String createSecureId(String prefix,Object o) { //e.g. <ds:KeyInfo Id="KI-9F6A3A6B473244859D59710683FABFE1"> if(prefix.equals("KI-")) return "KI-" + UUID.randomUUID().toString().replace("-","").toUpperCase(); //e.g. <wsse:SecurityTokenReference wsu:Id="STR-E6C0BA1EC73A4AB3BECFEBF6075EF175"> else if (prefix.equals("STR-")) return "STR-" + UUID.randomUUID().toString().replace("-","").toUpperCase(); //TODO why is there a condition where prefix.equals("X509") and o.toString() is the public cert? else return null; } //e.g. <ds:Signature Id="SIG-9850525DA06CE28ED91448475206411147" @Override public String createId(String prefix,Object o) { return "SIG-" + UUID.randomUUID().toString().replace("-","").toUpperCase(); } }; config.setIdAllocator(idAllocator ); //WSSecSignature wsSecSignature = new WSSecSignature(config); WSSecSignature wsSecSignature = new WSSecSignature(); wsSecSignature.setX509Certificate(signingCert); wsSecSignature.setUserInfo(alias,new String(keystorePassword.tocharArray())); wsSecSignature.setUseSingleCertificate(true); wsSecSignature.setKeyIdentifierType(WSConstants.X509_KEY_IDENTIFIER); //wsSecSignature.setKeyIdentifierType(WSConstants.SKI_KEY_IDENTIFIER); wsSecSignature.setDigestAlgo(WSConstants.SHA1); wsSecSignature.setSignatureAlgorithm(WSConstants.RSA_SHA1); wsSecSignature.setSigCanonicalization(WSConstants.C14N_EXCL_WITH_COMMENTS); try { Document document = toDocument(message); WSSecHeader secHeader = new WSSecHeader(document); //secHeader.setMustUnderstand(true); secHeader.insertSecurityHeader(); WSSecTimestamp timestamp = new WSSecTimestamp(); timestamp.setTimeToLive(signatureValidityTime); document = timestamp.build(document,secHeader); List<WSEncryptionPart> wsEncryptionParts = new ArrayList<WSEncryptionPart>(); //Very important,ordering of the parts is critical: refer to page 34 of the guide //for ACAGetTransmitterBulkRequestService,it is Timestamp,ACATransmitterManifestReqDtl,ACABusinessHeader if(clazz.equals(ACATransmitterManifestReqDtl.class)){ WSEncryptionPart timestampPart = new WSEncryptionPart("Timestamp",WSConstants.WSU_NS,""); //This is very important,Timestamp needs to be fist wsEncryptionParts.add(timestampPart); WSEncryptionPart aCATransmitterManifestReqDtlPart = new WSEncryptionPart( "ACATransmitterManifestReqDtl","urn:us:gov:treasury:irs:ext:aca:air:7.0",""); wsEncryptionParts.add(aCATransmitterManifestReqDtlPart); WSEncryptionPart aCABusinessHeaderPart = new WSEncryptionPart( "ACABusinessHeader","urn:us:gov:treasury:irs:msg:acabusinessheader",""); wsEncryptionParts.add(aCABusinessHeaderPart); } //for ACAGetTransmitterBulkRequestStatus,ACABusinessHeader,ACABulkRequestTransmitterStatusDetailRequest else if(clazz.equals(ACABulkRequestTransmitterStatusDetailRequest.class)){ WSEncryptionPart timestampPart = new WSEncryptionPart("Timestamp",Timestamp needs to be fist wsEncryptionParts.add(timestampPart); WSEncryptionPart aCABusinessHeaderPart = new WSEncryptionPart( "ACABusinessHeader",""); wsEncryptionParts.add(aCABusinessHeaderPart); WSEncryptionPart aCABulkRequestTransmitterStatusDetailRequestPart = new WSEncryptionPart( "ACABulkRequestTransmitterStatusDetailRequest","urn:us:gov:treasury:irs:msg:irstransmitterstatusrequest",""); wsEncryptionParts.add(aCABulkRequestTransmitterStatusDetailRequestPart); } wsSecSignature.getParts().addAll(wsEncryptionParts); Properties properties = new Properties(); properties.setProperty("org.apache.ws.security.crypto.provider","org.apache.ws.security.components.crypto.Merlin"); Crypto crypto = CryptoFactory.getInstance(properties); KeyStore keystore = KeyStore.getInstance("JKS"); java.io.FileInputStream fis = null; try { fis = new java.io.FileInputStream(System.getProperty("java.home") + "//lib//security//meckeystore.jks"); if(fis != null) { keystore.load(fis,keystorePassword.tocharArray()); } else { //TODO: replace with custom MEC exception throw new Exception("Unable to read keystore file."); } } finally { if (fis != null) { fis.close(); } } keystore.setKeyEntry(alias,signingKey,keystorePassword.tocharArray(),new Certificate[]{signingCert}); ((Merlin) crypto).setKeyStore(keystore); crypto.loadCertificate(new ByteArrayInputStream(signingCert.getEncoded())); document = wsSecSignature.build(document,crypto,secHeader); updateSOAPMessage(document,message); } catch (Exception e) { // throw new // WSSecurityException(WSSecurityException.Reason.SIGNING_ISSUE,e); e.printStackTrace(); } return message; } /** * Changes the SOAPMessage to a dom.Document. */ private static Document toDocument(SOAPMessage soapMsg) throws TransformerException,SOAPException,IOException { Source src = soapMsg.getSOAPPart().getContent(); TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); DOMResult result = new DOMResult(); transformer.transform(src,result); return (Document) result.getNode(); } //https://svn.apache.org/repos/asf/webservices/wss4j/branches/WSS4J_1_1_0_FINAL/test/wssec/SOAPUtil.java private static SOAPMessage updateSOAPMessage(Document doc,SOAPMessage message) throws Exception { DOMSource domSource = new DOMSource(doc); message.getSOAPPart().setContent(domSource); return message; }
这是示例SOAP请求
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:us:gov:treasury:irs:ext:aca:air:7.0" xmlns:urn1="urn:us:gov:treasury:irs:common"> <SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <wsse:Security SOAP-ENV: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"> <ds:Signature Id="SIG-d62ad452-5219-4baf-9708-3ae1d2cf7e92" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> <ds:SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments"/> <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> <ds:Reference URI="#TS-6450a75d-45e4-463b-a1e8-2d3ae3b4c57c"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"> <InclusiveNamespaces PrefixList="wsse SOAP-ENV soap urn urn1" xmlns="http://www.w3.org/2001/10/xml-exc-c14n#"/> </ds:Transform> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <ds:DigestValue>ojqiqHiXxPWIaEumCOO3bKJZ73A=</ds:DigestValue> </ds:Reference> <ds:Reference URI="#id-0EB7188D138D494EA44AC09FE03F6BEE"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"> <InclusiveNamespaces PrefixList="SOAP-ENV soap urn urn1" xmlns="http://www.w3.org/2001/10/xml-exc-c14n#"/> </ds:Transform> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <ds:DigestValue>cm3KGHFWHyJcBU9MEQzw6Ru04z0=</ds:DigestValue> </ds:Reference> <ds:Reference URI="#id-1183235E8ED44DE99B069411CD4837DC"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"> <InclusiveNamespaces PrefixList="SOAP-ENV soap urn urn1" xmlns="http://www.w3.org/2001/10/xml-exc-c14n#"/> </ds:Transform> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <ds:DigestValue>6nM3ONVPyHtiupcznWiixpNG82k=</ds:DigestValue> </ds:Reference> </ds:SignedInfo> <ds:SignatureValue>removed==</ds:SignatureValue> <ds:KeyInfo Id="KI-e6a6c681-ccf7-49ab-a37f-dac69c52d32a"> <wsse:SecurityTokenReference wsu:Id="STR-c1b4d47e-fda6-49b0-a58a-7df24ab43e13"> <wsse:KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">removed</wsse:KeyIdentifier> </wsse:SecurityTokenReference> </ds:KeyInfo> </ds:Signature> <wsu:Timestamp wsu:Id="TS-6450a75d-45e4-463b-a1e8-2d3ae3b4c57c"> <wsu:Created>2016-01-27T23:59:36.352Z</wsu:Created> <wsu:Expires>2016-01-28T00:59:36.352Z</wsu:Expires> </wsu:Timestamp> </wsse:Security> <ACATransmitterManifestReqDtl ns3:Id="id-0EB7188D138D494EA44AC09FE03F6BEE" xmlns="urn:us:gov:treasury:irs:ext:aca:air:7.0" xmlns:ns2="urn:us:gov:treasury:irs:common" xmlns:ns3="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <PaymentYr>2015</PaymentYr> <PriorYearDataInd>0</PriorYearDataInd> <ns2:EIN></ns2:EIN> <TransmissionTypeCd>O</TransmissionTypeCd> <TestFileCd>T</TestFileCd> <TransmitterNameGrp> <BusinessNameLine1Txt></BusinessNameLine1Txt> <BusinessNameLine2Txt>Health Systems</BusinessNameLine2Txt> </TransmitterNameGrp> <CompanyInformationGrp> <CompanyNm></CompanyNm> <MailingAddressGrp> <USAddressGrp> <AddressLine1Txt></AddressLine1Txt> <ns2:CityNm>Rockville</ns2:CityNm> <USStateCd>MD</USStateCd> <ns2:USZIPCd></ns2:USZIPCd> </USAddressGrp> </MailingAddressGrp> <ContactNameGrp> <PersonFirstNm></PersonFirstNm> <PersonMiddleNm>X</PersonMiddleNm> <PersonLastNm></PersonLastNm> </ContactNameGrp> <ContactPhoneNum></ContactPhoneNum> </CompanyInformationGrp> <VendorInformationGrp> <VendorCd>I</VendorCd> <ContactNameGrp> <PersonFirstNm></PersonFirstNm> <PersonMiddleNm></PersonMiddleNm> <PersonLastNm></PersonLastNm> </ContactNameGrp> <ContactPhoneNum></ContactPhoneNum> </VendorInformationGrp> <TotalPayeeRecordCnt>1000</TotalPayeeRecordCnt> <TotalPayerRecordCnt>1</TotalPayerRecordCnt> <SoftwareId></SoftwareId> <FormTypeCd>1094/1095B</FormTypeCd> <ns2:BinaryFormatCd>application/xml</ns2:BinaryFormatCd> <ns2:ChecksumAugmentationNum>5bae956d7c6a01c95ce570dd11debe78</ns2:ChecksumAugmentationNum> <ns2:AttachmentByteSizeNum>5938</ns2:AttachmentByteSizeNum> <DocumentSystemFileNm>1094B_Request_BBBBB_20151019T121002000Z.xml</DocumentSystemFileNm> </ACATransmitterManifestReqDtl> <urn2:ACABusinessHeader wsu:Id="id-1183235E8ED44DE99B069411CD4837DC" xmlns:urn2="urn:us:gov:treasury:irs:msg:acabusinessheader" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <urn:UniqueTransmissionId>d81ead9b-1223-4d28-8d46-f7af58710268:SYS12:BBBBB::T</urn:UniqueTransmissionId> <urn1:Timestamp>2016-01-27T23:59:36Z</urn1:Timestamp> </urn2:ACABusinessHeader> <Action xmlns="http://www.w3.org/2005/08/addressing">RequestSubmissionStatusDetail</Action>
真正对我们来说关键是来自IRS文档,因为我们使用的是Apache CXF v2.1.4:
5.4.2(来自IRS文件)
消息附件内容类型ISS-A2AAIR Web服务要求发送方使用SOAP-over-HTTP消息传递与MTOM发送XML数据文件.
在MTOM附件中编码的文件必须是未压缩的本机XML.用于标识的MTOM编码二进制对象的内容类型
Manifest标头必须是“application / xml”.表单数据文件的内容传输编码必须是7位.
在apache-cxf-3.1.4-src / core / src / main / java / org / apache / cxf / attachment / AttachmentSerializer.java中
194 private static void writeHeaders(String contentType,String attachmentId,195 Map<String,List<String>> headers,Writer writer) throws IOException { 196 // writer.write("\r\nContent-Type: "); 197 // writer.write(contentType); 198 writer.write("\r\nContent-Type: application/xml"); 199 // writer.write("\r\nContent-Transfer-Encoding: binary\r\n"); 200 writer.write("\r\nContent-Transfer-Encoding: 7bit\r\n");