web-dev-qa-db-ja.com

SSL接続中にPKIXパスの構築に失敗しました

CommWebと呼ばれるマーチャントアカウントと統合し、SSL投稿をそのURL( https://migs.mastercard.com.au/vpcdps )に送信します。投稿を送信しようとすると、次の例外が発生します。

Sun.security.validator.ValidatorException: PKIX path building failed: Sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

投稿を実行するコード(私が作成しなかったもので、すでにコードベースに存在しています)は次のとおりです。

public static HttpResponse sendHttpPostSSL(String url, Map<String, String> params) throws IOException {
    PostMethod postMethod = new PostMethod(url);
    for (Map.Entry<String, String> entry : params.entrySet()) {
        postMethod.addParameter(entry.getKey(), StringUtils.Nz(entry.getValue()));
    }

    HttpClient client = new HttpClient();
    int status = client.executeMethod(postMethod);
    if (status == 200) {
        StringBuilder resultBuffer = new StringBuilder();
        resultBuffer.append(postMethod.getResponseBodyAsString());
        return new HttpResponse(resultBuffer.toString(), "");
    } else {
        throw new IOException("Invalid response code: " + status);
    }
}

マーチャントアカウント統合のドキュメントでは、証明書については何も述べられていません。彼らは盲目的に証明書を受け入れるように見えるいくつかのサンプルJSPコードを提供しました:

<%! // Define Static Constants
    // ***********************
public static X509TrustManager s_x509TrustManager = null;
public static SSLSocketFactory s_sslSocketFactory = null;

static {
        s_x509TrustManager = new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[] {}; } 
        public boolean isClientTrusted(X509Certificate[] chain) { return true; } 
        public boolean isServerTrusted(X509Certificate[] chain) { return true; } 
    };

    Java.security.Security.addProvider(new com.Sun.net.ssl.internal.ssl.Provider());
    try {
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, new X509TrustManager[] { s_x509TrustManager }, null);
        s_sslSocketFactory = context.getSocketFactory();
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException(e.getMessage());
    }
}

...
...
           // write output to VPC
            SSLSocket ssl = (SSLSocket)s_sslSocketFactory.createSocket(s, vpc_Host, vpc_Port, true);
            ssl.startHandshake();
            os = ssl.getOutputStream();
            // get response data from VPC
            is = ssl.getInputStream();
...
...
%>

私たちのwebappにはキーストアがあり、keytoolコマンドを使用して証明書(firefoxからエクスポートしたもの)を追加しようとしましたが、機能せず、同じエラーが発生しました。私はウェブで解決策を試しました(キーをインポートしてSystem.setProperty)ですが、これはちょっと不格好なようで、うまくいきませんでした(NoSuchAlgorithmErrorをください)。どんな助けでもありがたいです!

17
Vivin Paliath

明らかに、valicertクラス3 CA証明書はデフォルトのトラストストアにありません(これはおそらくJRE lib/securityディレクトリーのcacertsファイルですが、詳細については JSSEのドキュメント を参照してください)。

この証明書をcacertsファイルに追加することもできますが、お勧めしません。代わりに、独自のトラストストアファイル(cacertsファイルのコピーでもかまいません)を作成し、これにvalicertルートcaを追加する必要があると思います。次に、javax.net.ssl.trustStoreシステムプロパティでこのファイルをポイントします。

私はこの答えを実際に行ったことで更新する必要があると思います。 GregSが提供したドキュメントを使用して、valicertの信頼マネージャーを作成しました。トラストマネージャーで、証明書ファイルをロードします。

public class ValicertX509TrustManager implements X509TrustManager {

    X509TrustManager pkixTrustManager;

    ValicertX509TrustManager() throws Exception {

        String valicertFile = "/certificates/ValicertRSAPublicRootCAv1.cer";
        String commwebDRFile = "/certificates/DR_10570.migs.mastercard.com.au.crt";
        String commwebPRODFile = "/certificates/PROD_10549.migs.mastercard.com.au.new.crt";

        Certificate valicert = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(valicertFile));
        Certificate commwebDR = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(commwebDRFile));
        Certificate commwebPROD = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(commwebPRODFile));

        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(null, "".toCharArray());
        keyStore.setCertificateEntry("valicert", valicert);
        keyStore.setCertificateEntry("commwebDR", commwebDR);
        keyStore.setCertificateEntry("commwebPROD", commwebPROD);

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
        trustManagerFactory.init(keyStore);

        TrustManager trustManagers[] = trustManagerFactory.getTrustManagers();

        for(TrustManager trustManager : trustManagers) {
            if(trustManager instanceof X509TrustManager) {
                pkixTrustManager = (X509TrustManager) trustManager;
                return;
            }
        }

        throw new Exception("Couldn't initialize");
    }

    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        pkixTrustManager.checkServerTrusted(chain, authType);
    }

    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        pkixTrustManager.checkServerTrusted(chain, authType);
    }

    public X509Certificate[] getAcceptedIssuers() {
        return pkixTrustManager.getAcceptedIssuers();
    }
}

次に、この信頼マネージャーを使用して、ソケットファクトリを作成する必要がありました。

public class ValicertSSLProtocolSocketFactory implements ProtocolSocketFactory {

    private SSLContext sslContext = null;

    public ValicertSSLProtocolSocketFactory() {
        super();
    }

    private static SSLContext createValicertSSLContext() {
        try {
            ValicertX509TrustManager valicertX509TrustManager = new ValicertX509TrustManager();
            SSLContext context = SSLContext.getInstance("TLS");
            context.init(null, new ValicertX509TrustManager[] { valicertX509TrustManager}, null);
            return context;
        }

        catch(Exception e) {
            Log.error(Log.Context.Net, e);
            return null;
        }
    }

    private SSLContext getSSLContext() {
        if(this.sslContext == null) {
            this.sslContext = createValicertSSLContext();
        }

        return this.sslContext;
    }

    public Socket createSocket(String Host, int port, InetAddress clientHost, int clientPort) throws IOException {
        return getSSLContext().getSocketFactory().createSocket(Host, port, clientHost, clientPort);
    }

    public Socket createSocket(final String Host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params) throws IOException {
        if(params == null) {
            throw new IllegalArgumentException("Parameters may not be null");
        }

        int timeout = params.getConnectionTimeout();
        SocketFactory socketFactory = getSSLContext().getSocketFactory();

        if(timeout == 0) {
            return socketFactory.createSocket(Host, port, localAddress, localPort);
        }

        else {
            Socket socket = socketFactory.createSocket();
            SocketAddress localAddr = new InetSocketAddress(localAddress, localPort);
            SocketAddress remoteAddr = new InetSocketAddress(Host, port);
            socket.bind(localAddr);
            socket.connect(remoteAddr, timeout);
            return socket;
        }
    }

    public Socket createSocket(String Host, int port) throws IOException {
        return getSSLContext().getSocketFactory().createSocket(Host, port);
    }

    public Socket createSocket(Socket socket, String Host, int port, boolean autoClose) throws IOException {
        return getSSLContext().getSocketFactory().createSocket(socket, Host, port, autoClose);
    }

    public boolean equals(Object obj) {
        return ((obj != null) && obj.getClass().equals(ValicertSSLProtocolSocketFactory.class));
    }

    public int hashCode() {
        return ValicertSSLProtocolSocketFactory.class.hashCode();
    }
}

今、私は新しいプロトコルを登録するだけです:

Protocol.registerProtocol("vhttps", new Protocol("vhttps", new ValicertSSLProtocolSocketFactory(), 443));
PostMethod postMethod = new PostMethod(url);
for (Map.Entry<String, String> entry : params.entrySet()) {
    postMethod.addParameter(entry.getKey(), StringUtils.Nz(entry.getValue()));
}

HttpClient client = new HttpClient();
int status = client.executeMethod(postMethod);
if (status == 200) {
    StringBuilder resultBuffer = new StringBuilder();
    resultBuffer.append(postMethod.getResponseBodyAsString());
    return new HttpResponse(resultBuffer.toString(), "");
} else {
    throw new IOException("Invalid response code: " + status);
}

唯一の欠点は、この特定の証明書用に特定のプロトコル(vhttps)を作成する必要があったことです。

7
Vivin Paliath