webservice复杂加密签名(2)java调用

依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-ws</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>wsdl4j</groupId>
    <artifactId>wsdl4j</artifactId>
</dependency>

WSDL转java

在这里插入图片描述

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <includeSystemScope>true</includeSystemScope>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.jvnet.jaxb2.maven2</groupId>
            <artifactId>maven-jaxb2-plugin</artifactId>
            <version>0.13.2</version>
            <executions>
                <execution>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <schemaLanguage>WSDL</schemaLanguage>
                <generateDirectory>${basedir}/src/main/java</generateDirectory>
                <schemas>
                    <schema>
                        <fileset>
                            <directory>${basedir}/src/main/resources/wsdl</directory>
                            <includes>
                                <include>*.wsdl</include>
                                <include>*.wsd</include>
                            </includes>
                        </fileset>
                    </schema>
                </schemas>
            </configuration>
        </plugin>
    </plugins>
</build>
mvn org.jvnet.jaxb2.maven2:maven-jaxb2-plugin:0.13.2:generate

在这里插入图片描述

HTTPS签名

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.ws.transport.WebServiceMessageSender;
import org.springframework.ws.transport.http.ClientHttpRequestMessageSender;

import javax.net.ssl.SSLContext;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.KeyStore;

@Slf4j
@Configuration
public class SoapConfig {

    @Value("${mastercard.keystore.client.path}")
    private String clientKeystorePath;
    @Value("${mastercard.keystore.client.password}")
    private String clientPassword;

    @Bean
    public WebServiceMessageSender webServiceMessageSender() throws Exception {
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        byte[] keystoreBytes;
        try (InputStream inputStream = resolver.getResource(clientKeystorePath).getInputStream()) {
            keystoreBytes = IOUtils.toByteArray(inputStream);
        } catch (Exception e) {
            log.error("https秘钥文件client.jks读取异常, keystore: {}", clientKeystorePath);
            throw e;
        }

        KeyStore keystore = KeyStore.getInstance("PKCS12");
        keystore.load(new ByteArrayInputStream(keystoreBytes), clientPassword.toCharArray());

        SSLContext sslContext = SSLContexts.custom()
                .loadKeyMaterial(keystore, clientPassword.toCharArray())
                .build();
        SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(csf)
                .build();
        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(httpClient);
        requestFactory.setConnectTimeout(10_000);
        requestFactory.setReadTimeout(10_000);
        ClientHttpRequestMessageSender messageSender = new ClientHttpRequestMessageSender();
        messageSender.setRequestFactory(requestFactory);
        return messageSender;
    }

}

报文签名

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 {
        // Create the security element
        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");

        // (i) Extract the certificate from the .p12 file.
        java.security.cert.Certificate cert = getCertificate();

        // (ii) Add Binary Security Token. The base64 encoded value of the ROS digital certificate.
        SOAPElement binarySecurityToken = addBinarySecurityToken(securityElement, soapMessage, cert);

        //(iii) Add Timestamp element
        SOAPElement timestamp = addTimestamp(securityElement, soapMessage);

        // (iv) Add signature element
        // Get private key from ROS digital certificate
        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());

        //Digest method
        javax.xml.crypto.dsig.DigestMethod digestMethod = xmlSignatureFactory.newDigestMethod("http://www.w3.org/2001/04/xmlenc#sha512", null);
        ArrayList<Transform> transformList = new ArrayList<>();

        //Transform
        Transform envTransform = xmlSignatureFactory.newTransform("http://www.w3.org/2001/10/xml-exc-c14n#", (TransformParameterSpec) null);
        transformList.add(envTransform);

        //References
        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");

        //These are required for new Java versions
        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;
    }

}

调用层

import com.mastercard.mcrewards.ws.common.EmptyElement;
import com.mastercard.mcrewards.ws.diagnostic.ApplicationStatus;
import com.mastercard.mcrewards.ws.diagnostic.CurrentVersion;
import com.mastercard.mcrewards.ws.diagnostic.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.stereotype.Service;
import org.springframework.ws.client.core.WebServiceMessageCallback;
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
import org.springframework.ws.transport.WebServiceMessageSender;

import javax.xml.bind.JAXBElement;

@Service
public class SoapDiagnosticService extends WebServiceGatewaySupport {

    private static final ObjectFactory factory = new com.mastercard.mcrewards.ws.diagnostic.ObjectFactory();
    private static final String packagePath = "com.mastercard.mcrewards.ws.diagnostic";

    private SoapSignService soapSignService;

    @Autowired
    public SoapDiagnosticService(WebServiceMessageSender messageSender,
                                 SoapSignService soapSignService,
                                 @Value("${mastercard.service.diagnosticServiceLink}") String link) {
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        marshaller.setContextPath(packagePath);
        this.setDefaultUri(link);
        this.setMarshaller(marshaller);
        this.setUnmarshaller(marshaller);
        this.setMessageSender(messageSender);
        this.soapSignService = soapSignService;
    }

    public String doEcho(String request) {
        JAXBElement<String> source = factory.createDoEcho(request);
        WebServiceMessageCallback wsCallback = message -> soapSignService.sign(message);
        JAXBElement response = (JAXBElement) getWebServiceTemplate().marshalSendAndReceive(source, wsCallback);
        return (String) response.getValue();
    }

}
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页