如何将 pfx 文件转换为 jks,然后使用它通过使用从 wsdl 生成的 类 来签署传出的 soap 请求

How to convert a pfx file into jks and then use it to sign an outgoing soap request by using the classes generated from a wsdl

我正在寻找一个代码示例,它展示了如何使用 PFX 证书通过 SSL 访问安全的 Web 服务。 我有证书及其密码,我首先使用下面提到的命令创建一个 KeyStore 实例。

keytool -importkeystore -destkeystore "C:\Program Files\Java\jdk1.8.0_131\jre\lib\security\dvs.keystore" -srckeystore "C:\Program Files\Java\jdk1.8.0_131\jre\lib\security\key.pfx" -srcstoretype pkcs12 -deststoretype JKS -srcstorepass *******

然后我用 wsimport -keep -verbose -extension https://sandpit.dvshub.com.au:19443/Bus/VerificationServiceBus.svc?wsdl 命令生成 Java 个文件。

之后我创建了一个主程序 class,我在其中指定了几个参数,例如这些证书的位置。

        System.setProperty("javax.net.ssl.trustStore", trustStoreFile);
        System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword);

        System.setProperty("javax.net.ssl.keyStore", certificateFile);
        System.setProperty("javax.net.ssl.keyStorePassword", certificatePassword);
        System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");

        System.setProperty("javax.net.ssl.keyStore", "C:\Users\Administrator\Desktop\dvs\key.pfx");
        System.setProperty("javax.net.ssl.keyStoreType", "PKCS12");
        System.setProperty("javax.net.ssl.keyStorePassword", certificatePassword);

然后我最终调用了 wsimport 使用它生成的服务创建的 web 方法。

CreatedService service = ServiceFactory/Port/Creator.getCreatedService(); // Where 'CreatedService' and 'ServiceFactory/Port/Creator' were created by wsimport: this code entirely depends on the WSDL provided.
service.[ws method](...);

然后我创建了一个处理程序来跟踪在 header 中传递的内容,但我根本看不到任何签名被添加到它。我在这里错过了什么吗?我只收到请求超时错误。

我在 Soap UI 中有一个它的工作示例,所以我知道该服务 运行 是正确的。

如能提供任何帮助,我们将不胜感激。请为我指明正确的方向,因为此时我已准备好尝试任何事情。

提前致谢。

[编辑] WSO2 应用程序服务器是要走的路吗:Reference

这是我采用当前方法的地方Reference

所以我一直在寻找一种方法来签署我的 soap 请求,我将详细介绍如何使用 wsdl 生成 Java classes向我提供了我如何设法从提供给我的 pfx 文件生成一个 Java 密钥库,然后设法使用它签署 soap 请求。

WSDL 到 Java classes:

所以我将隐藏在 ssl 证书后面的 wsdl 的内容复制到一个文件中,然后使用下面的 pom 中定义的插件生成 Java classes。然后我将这些 classes 从目标文件夹移动到 src 目录。

pom.xml

 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.javacodegeeks.examples.jaxws.client</groupId>
    <artifactId>JavaWsClient</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-codegen-plugin</artifactId>
                <version>3.1.12</version>
                <executions>
                    <execution>
                        <id>generate-sources</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <sourceRoot>${project.build.directory}/generated/cxf</sourceRoot>
                            <wsdlOptions>
                                <wsdlOption>
                                    <wsdl>${basedir}/src/main/resources/wsdl.xml</wsdl>
                                </wsdlOption>
                            </wsdlOptions>
                        </configuration>
                        <goals>
                            <goal>wsdl2java</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>3.1.12</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>3.1.12</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-ws-security</artifactId>
            <version>3.1.12</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.cxf/cxf-rt-transports-http-jetty -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http-jetty</artifactId>
            <version>3.1.12</version>
        </dependency>
<!--

        <dependency>
            <groupId>org.springframework.ws</groupId>
            <artifactId>spring-ws-security</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.santuario</groupId>
                    <artifactId>xmlsec</artifactId>
                </exclusion>
            </exclusions>
        </dependency>-->
        <dependency>
            <groupId>org.apache.santuario</groupId>
            <artifactId>xmlsec</artifactId>
            <version>2.0.8</version>
        </dependency>


    </dependencies>
</project>

使用 mvn generate-resources 会在目标文件夹中为您创建 Java classes。

下一步是将提供给我的 pfx 文件转换为 JavaKeyStore 'JKS'。下面提到的步骤。您将必须下载 weblogic 以获得转换所需的 jar。

1.Run 以下 OpenSSL 命令从 .pfx 文件中提取您的证书和密钥: openssl pkcs12 -in yourfilename.pfx -out tempcertfile.crt -nodes 您现在应该有一个名为 tempcertfile.crt 的文件。使用文本编辑器(例如写字板)打开此文件。您将首先看到列出的私钥,然后是您的证书信息。

-----BEGIN RSA PRIVATE KEY-----
(Block of Encrypted Text)
-----END RSA PRIVATE KEY-----

2.Cut 并将所有私钥(包括 BEGIN 和 END 标记)粘贴到一个新的文本文件中,并将其另存为 your_domain_name.key

3.The 个留在您的 tempcertfile.crt 中的证书将按以下顺序排列:服务器证书、根证书和中间证书。但是,根据您的 .pfx 导出,文件中可能有 2-4 个证书。只要你正确导出了证书,这个文件里的就是你应该有的证书。

4.Make 确保私钥已删除(不仅仅是复制和粘贴),然后将文件另存为 your_domain_name.pem.

5通过运行在keytool中将以下两行作为一个命令创建身份证书密钥库:

java utils.ImportPrivateKey -keystore new_identity_keystore.jks -storepass 
YOURPASSWORD -storetype JKS -keypass YOURPASSWORD -alias 
server -certfile tempcertfile.crt -keyfile your_domain_name.key 
-keyfilepass PFXPASSWORD

记得用您的密码替换 YOURPASSWORD。同时将 PFXPASSWORD 替换为您在创建 .pfx 文件时创建的密码。

Reference

下面是我根据参考执行的命令。

openssl pkcs12 -in "C:\Program Files\Java\jdk1.8.0_131\jre\lib\security\file.pfx" -out "C:\Program Files\Java\jdk1.8.0_131\jre\lib\security\tempcertfile.crt" -nodes

openssl x509 -outform der -in "C:\Program Files\Java\jdk1.8.0_131\jre\lib\security\file.pem" -out "C:\Program Files\Java\jdk1.8.0_131\jre\lib\security\file.der"


java -cp C:\Oracle\Middleware\Oracle_Home\wlserver\server\lib\weblogic.jar utils.ImportPrivateKey -keystore "C:\Program Files\Java\jdk1.8.0_131\jre\lib\security\file.jks" -storepass mypass-storetype JKS -keypass mypass-alias myalias -certfile "C:\Program Files\Java\jdk1.8.0_131\jre\lib\security\file.pem" -keyfile "C:\Program Files\Java\jdk1.8.0_131\jre\lib\security\file.key" -keyfilepass mypass

下一步是使用 jks 并使用 cfx 签署我的传出请求。这是 Java class 及其配置文件。

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package dvstest;


import dvs.common._2014._06.contract.data.Gender;
import dvs.common._2014._06.contract.data.RegistrationState;
import dvs.common._2014._06.contract.data.manager.*;
import dvs.common._2014._06.contract.service.manager.IVerification;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.apache.cxf.ws.addressing.WSAddressingFeature;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.apache.wss4j.dom.WSConstants;
import org.apache.wss4j.dom.handler.WSHandlerConstants;

import javax.xml.bind.JAXBElement;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author Sadiq
 */
public class DVSTest {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        try {

            // These params are used to print the soap request going in and out.
            System.setProperty("com.sun.xml.ws.transport.http.client.HttpTransportPipe.dump", "true");
            System.setProperty("com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump", "true");
            System.setProperty("com.sun.xml.ws.transport.http.HttpAdapter.dump", "true");
            System.setProperty("com.sun.xml.internal.ws.transport.http.HttpAdapter.dump", "true");

            //Path to java keystore which holds the ssl certificate, might come in handy later on.
            /*String trustStoreFile = "C:\Program Files\Java\jdk1.8.0_131\jre\lib\security\cacerts";
            String trustStorePassword = "changeit";


            System.setProperty("javax.net.ssl.trustStore", trustStoreFile);
            System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword);
            System.setProperty("javax.net.ssl.trustStoreType", "JKS");
            System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");*/


            /*
            This is how we can extra namespaces if needed.

            Map<String, String> nsMap = new HashMap();

            nsMap.put("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
            nsMap.put("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
            nsMap.put("man", "http://DVS/Common/2014/06/Contract/Service/Manager");
            nsMap.put("man1", "http://DVS/Common/2014/06/Contract/Data/Manager");
            nsMap.put("ds", "http://www.w3.org/2000/09/xmldsig#");
            nsMap.put("ec", "http://www.w3.org/2001/10/xml-exc-c14n#");

            client.getRequestContext().put("soap.env.ns.map", nsMap);
            */


            //Creating a factory and setting the service interface using which we can make soap requests.
            JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
            factory.setServiceClass(IVerification.class);

            //Path to endpoint
            //You can get this path by looking inside the wsdl
            factory.setAddress("https://urlhere/Https");

            //Pointing the post request to be soap12 compliant
            factory.setBindingId("http://schemas.xmlsoap.org/wsdl/soap12/");

            //Adding address feature to the outgoing request, this will add <To><MessageId><ReplyTo> part to soap request.
            factory.getFeatures().add(new WSAddressingFeature());

            //Creating a port for the verification interface using the factory.
            IVerification port = (IVerification) factory.create();

            //Creating client, this will be used to specify various outgoing props.
            Client client = ClientProxy.getClient(port);

            //Setting content type and creating a conduit.
            HTTPConduit http = (HTTPConduit) client.getConduit();
            HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
            httpClientPolicy.setContentType("application/soap+xml");
            http.setClient(httpClientPolicy);

            //Endpoint fetched using client
            Endpoint cxfEndpoint = client.getEndpoint();

            //Setting cfx related props
            Map<String, Object> outProps = new HashMap<String, Object>();
            outProps.put(WSHandlerConstants.ACTION, "Signature Timestamp");
            outProps.put(WSHandlerConstants.USER, "myalias");
            outProps.put(WSHandlerConstants.SIG_PROP_FILE, "client_sign.properties");
            //Used to add the digest part to the soap post request
            outProps.put(WSHandlerConstants.SIG_KEY_ID, "DirectReference");
            //Used to sign the <To> element.
            outProps.put(WSHandlerConstants.SIGNATURE_PARTS, "{Element}{http://www.w3.org/2005/08/addressing}To");
            // Password type : plain text
            outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
            // for hashed password use:
            //properties.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_DIGEST);
            // Callback used to retrieve password for given user.
            outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,
                    ClientPasswordCallback.class.getName());

            //Setting props to post request.
            WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
            cxfEndpoint.getOutInterceptors().add(wssOut);


            System.out.println(passportRequest(port).getVerificationResultCode());

            System.out.println(driverLicenseRequest(port).getVerificationResultCode());



        } catch (Exception ex) {
            Logger.getLogger(DVSTest.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    /**
     * Sets properties to PassportRequest and makes a soap request using the IVerification object.
     *
     * @param port Needs a IVerification object created by the factory.
     * @return VerificationResponse as a response of soap request.
     * @throws Exception
     */
    public static VerificationResponse passportRequest(IVerification port) throws Exception {

        //Creating a passport request
        PassportRequest request = new PassportRequest();

        //Creating a DVSDate object and the creating a jaxb element to be assigned to the PassportRequest object.
        DVSDate date = new DVSDate();
        date.setDay(1);
        date.setMonth(1);
        date.setYear(2017);
        ObjectFactory objectFactory = new ObjectFactory();
        JAXBElement<DVSDate> documentRequest = objectFactory.createDVSDate(date);
        request.setBirthDate(documentRequest);

        request.setDocumentTypeCode(DocumentType.PP);
        JAXBElement<String> familyName = objectFactory.createCertificateRequestFamilyName2("D");
        request.setFamilyName(familyName);
        JAXBElement<String> givenName = objectFactory.createCertificateRequestGivenName2("T");
        request.setGivenName(givenName);
        request.setOriginatingAgencyCode("1");
        GregorianCalendar c = new GregorianCalendar();
        c.setTime(new Date(System.currentTimeMillis()));
        XMLGregorianCalendar requestDate = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
        request.setRequestDateTime(requestDate);
        request.setVerificationRequestNumber("1");
        request.setVersionNumber("1");
        JAXBElement<Gender> gender = objectFactory.createPassportRequestGender(Gender.M);
        request.setGender(gender);
        request.setTravelDocumentNumber("1");

        return port.verifyDocument(request);
    }

    /**
     * Sets properties to DriverLicenseRequest and makes a soap request using the IVerification object.
     *
     * @param port Needs a IVerification object created by the factory.
     * @return VerificationResponse as a response of soap request.
     * @throws Exception
     */
    public static VerificationResponse driverLicenseRequest(IVerification port) throws Exception {

        //Creating a passport request
        DriverLicenceRequest request = new DriverLicenceRequest();

        //Creating a DVSDate object and the creating a jaxb element to be assigned to the PassportRequest object.
        DVSDate date = new DVSDate();
        date.setDay(1);
        date.setMonth(1);
        date.setYear(2017);
        ObjectFactory objectFactory = new ObjectFactory();
        JAXBElement<DVSDate> documentRequest = objectFactory.createDVSDate(date);
        request.setBirthDate(documentRequest);

        request.setDocumentTypeCode(DocumentType.DL);
        JAXBElement<String> familyName = objectFactory.createCertificateRequestFamilyName2("D");
        request.setFamilyName(familyName);
        JAXBElement<String> givenName = objectFactory.createCertificateRequestGivenName2("T");
        request.setGivenName(givenName);
        request.setOriginatingAgencyCode("1");
        GregorianCalendar c = new GregorianCalendar();
        c.setTime(new Date(System.currentTimeMillis()));
        XMLGregorianCalendar requestDate = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
        request.setRequestDateTime(requestDate);
        request.setVerificationRequestNumber("1");
        request.setVersionNumber("1");
        request.setLicenceNumber("1");
        JAXBElement<String> middleName = objectFactory.createDriverLicenceRequestMiddleName("Joseph");
        request.setMiddleName(middleName);

        dvs.common._2014._06.contract.data.ObjectFactory objectFactoryData = new dvs.common._2014._06.contract.data.ObjectFactory();
        JAXBElement<RegistrationState> registrationState = objectFactoryData.createRegistrationState(RegistrationState.NSW);
        request.setStateOfIssue(registrationState.getValue());
        JAXBElement<Gender> gender = objectFactory.createPassportRequestGender(Gender.M);




        return port.verifyDocument(request);
    }


}

client_sign.properties 文件:

org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=mypass
org.apache.ws.security.crypto.merlin.keystore.alias=myalias
org.apache.ws.security.crypto.merlin.keystore.file=C:\Program Files\Java\jdk1.8.0_131\jre\lib\security\file.jks

最后但并非最不重要的密码回调处理程序。

package dvstest;

import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.wss4j.common.ext.WSPasswordCallback;

public class ClientPasswordCallback implements CallbackHandler {

    @Override
    public void handle(Callback[] callbacks) throws IOException,
            UnsupportedCallbackException {

        WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

        // set the password for our message.
        pc.setPassword("mypass");
    }

}

我希望这对某人有所帮助。我花了一些时间来收集所有需要的信息。

Reference2 Reference3