1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
| import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.stereotype.Component; import org.springframework.ws.WebServiceMessage; import org.springframework.ws.soap.saaj.SaajSoapMessage;
import javax.xml.crypto.dom.DOMStructure; import javax.xml.crypto.dsig.Reference; import javax.xml.crypto.dsig.SignedInfo; import javax.xml.crypto.dsig.Transform; import javax.xml.crypto.dsig.XMLSignatureFactory; import javax.xml.crypto.dsig.dom.DOMSignContext; import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory; import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; import javax.xml.crypto.dsig.spec.TransformParameterSpec; import javax.xml.soap.*; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.security.KeyStore; import java.security.PrivateKey; import java.security.Provider; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Base64;
@Slf4j @Component public class SoapSignService {
private final KeyStore keystore; private final String password; private final String keystoreAlias;
@Value("${mastercard.system.institutionName}") private String institutionName;
@Autowired public SoapSignService(@Value("${mastercard.keystore.sign.path}") String path, @Value("${mastercard.keystore.sign.password}") String password, @Value("${mastercard.keystore.sign.alias}") String keystoreAlias) throws Exception { PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); byte[] keystoreBytes; try (InputStream inputStream = resolver.getResource(path).getInputStream()) { keystoreBytes = IOUtils.toByteArray(inputStream); } catch (Exception e) { log.error("签名秘钥文件signing.jks读取异常, keystore: {}", path); throw e; }
this.keystore = KeyStore.getInstance("PKCS12"); this.keystore.load(new ByteArrayInputStream(keystoreBytes), password.toCharArray()); this.password = password; this.keystoreAlias = keystoreAlias; }
private PrivateKey getKeyFormCert() throws Exception { return (PrivateKey) keystore.getKey(keystoreAlias, password.toCharArray()); }
private java.security.cert.Certificate getCertificate() throws Exception { return keystore.getCertificate(keystoreAlias); }
public void sign(WebServiceMessage message) { sign("", message); }
public void sign(String appId, WebServiceMessage message) { this.sign(appId, ((SaajSoapMessage) message).getSaajMessage()); }
public void sign(String appId, SOAPMessage soapMessage) { try { SOAPEnvelope soapEnvelope = soapMessage.getSOAPPart().getEnvelope(); soapEnvelope.setPrefix("soapenv"); soapEnvelope.removeNamespaceDeclaration("SOAP-ENV"); soapEnvelope.addNamespaceDeclaration("com", "http://common.ws.mcrewards.mastercard.com/");
SOAPHeader header = soapMessage.getSOAPHeader(); header.setPrefix("soapenv"); SOAPElement identity = header.addChildElement("identity", "com"); identity.addAttribute(soapEnvelope.createName("Id", "wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"), "IDENTITY");
SOAPElement appID = identity.addChildElement("appID"); appID.addTextNode(appId); SOAPElement institutionName = identity.addChildElement("institutionName"); institutionName.addTextNode(this.institutionName);
SOAPBody soapBody = soapMessage.getSOAPBody(); soapBody.setPrefix("soapenv"); soapBody.addAttribute(soapEnvelope.createName("Id", "wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"), "Body");
signSOAPMessage(soapMessage); } catch (Exception e) { log.error(e.getMessage(), e); throw new RuntimeException(e); } }
private void signSOAPMessage(SOAPMessage soapMessage) throws Exception { SOAPElement soapHeader = soapMessage.getSOAPHeader(); SOAPElement securityElement = soapHeader.addChildElement("Security", "wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); securityElement.addNamespaceDeclaration("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
java.security.cert.Certificate cert = getCertificate();
SOAPElement binarySecurityToken = addBinarySecurityToken(securityElement, soapMessage, cert);
SOAPElement timestamp = addTimestamp(securityElement, soapMessage);
PrivateKey key = getKeyFormCert(); SOAPElement securityTokenReference = addSecurityToken(securityElement); addSignature(securityElement, key, securityTokenReference, soapMessage.getSOAPBody(), timestamp, binarySecurityToken); }
private SOAPElement addSecurityToken(SOAPElement signature) throws SOAPException { SOAPElement securityTokenReference = signature.addChildElement("SecurityTokenReference", "wsse"); SOAPElement reference = securityTokenReference.addChildElement("Reference", "wsse"); reference.setAttribute("URI", "#X509Token"); return securityTokenReference; }
private void addSignature(SOAPElement signatureElement, PrivateKey privateKey, SOAPElement securityTokenReference, SOAPBody soapBody, SOAPElement timestamp, SOAPElement binarySecurityToken) throws Exception { String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI"); XMLSignatureFactory xmlSignatureFactory = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());
javax.xml.crypto.dsig.DigestMethod digestMethod = xmlSignatureFactory.newDigestMethod("http://www.w3.org/2001/04/xmlenc#sha512", null); ArrayList<Transform> transformList = new ArrayList<>();
Transform envTransform = xmlSignatureFactory.newTransform("http://www.w3.org/2001/10/xml-exc-c14n#", (TransformParameterSpec) null); transformList.add(envTransform);
ArrayList<Reference> referenceList = new ArrayList<>(); Reference x509 = xmlSignatureFactory.newReference("#X509Token", digestMethod, transformList, null, null); Reference refTS = xmlSignatureFactory.newReference("#TS", digestMethod, transformList, null, null); Reference refBody = xmlSignatureFactory.newReference("#Body", digestMethod, transformList, null, null); Reference header = xmlSignatureFactory.newReference("#IDENTITY", digestMethod, transformList, null, null); referenceList.add(x509); referenceList.add(refTS); referenceList.add(refBody); referenceList.add(header);
javax.xml.crypto.dsig.CanonicalizationMethod cm = xmlSignatureFactory.newCanonicalizationMethod("http://www.w3.org/2001/10/xml-exc-c14n#", (C14NMethodParameterSpec) null); javax.xml.crypto.dsig.SignatureMethod sm = xmlSignatureFactory.newSignatureMethod("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", null); SignedInfo signedInfo = xmlSignatureFactory.newSignedInfo(cm, sm, referenceList);
DOMSignContext signContext = new DOMSignContext(privateKey, signatureElement); signContext.setDefaultNamespacePrefix("ds"); signContext.putNamespacePrefix("http://www.w3.org/2000/09/xmldsig#", "ds");
signContext.setIdAttributeNS(soapBody, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "Id"); signContext.setIdAttributeNS(timestamp, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "Id"); signContext.setIdAttributeNS(binarySecurityToken, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "Id");
KeyInfoFactory keyFactory = KeyInfoFactory.getInstance(); DOMStructure domKeyInfo = new DOMStructure(securityTokenReference); javax.xml.crypto.dsig.keyinfo.KeyInfo keyInfo = keyFactory.newKeyInfo(java.util.Collections.singletonList(domKeyInfo)); javax.xml.crypto.dsig.XMLSignature signature = xmlSignatureFactory.newXMLSignature(signedInfo, keyInfo); signContext.setBaseURI(""); signature.sign(signContext); }
private SOAPElement addTimestamp(SOAPElement securityElement, SOAPMessage soapMessage) throws SOAPException { SOAPElement timestamp = securityElement.addChildElement("Timestamp", "wsu"); SOAPEnvelope soapEnvelope = soapMessage.getSOAPPart().getEnvelope(); timestamp.addAttribute(soapEnvelope.createName("Id", "wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"), "TS");
String DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ssX"; DateTimeFormatter timeStampFormatter = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN); ZonedDateTime zonedDateTime = ZonedDateTime.now(); timestamp.addChildElement("Created", "wsu").setValue(timeStampFormatter.format(zonedDateTime.toInstant().atZone(ZoneId.of("UTC")))); timestamp.addChildElement("Expires", "wsu").setValue(timeStampFormatter.format(zonedDateTime.plusSeconds(60).toInstant().atZone(ZoneId.of("UTC")))); return timestamp; }
private SOAPElement addBinarySecurityToken(SOAPElement securityElement, SOAPMessage soapMessage, java.security.cert.Certificate cert) throws Exception { byte[] certByte = cert.getEncoded(); SOAPElement binarySecurityToken = securityElement.addChildElement("BinarySecurityToken", "wsse");
binarySecurityToken.setAttribute("ValueType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"); binarySecurityToken.setAttribute("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"); SOAPEnvelope soapEnvelope = soapMessage.getSOAPPart().getEnvelope();
binarySecurityToken.addAttribute(soapEnvelope.createName("Id", "wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"), "X509Token"); binarySecurityToken.addTextNode(Base64.getEncoder().encodeToString(certByte)); return binarySecurityToken; }
}
|