如何使用 java 获取安全网页的 public 密钥

How can I get the public key of a secure webpage using java

我需要通过 java 以编程方式获取安全网站的 public 密钥。我也读过 this, this, this and this 和其他书。 但是我还没有找到通过 java.

获取它的解决方案

编辑:::

根据 Zielu 的回答,我编写了以下程序:

import java.security.PublicKey;
import java.security.cert.Certificate;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

public class RetrievePublicKey {

    private static PublicKey getKey(String hostname, int port) throws Exception {
        SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();        
        SSLSocket socket = (SSLSocket) factory.createSocket(hostname, port);
        socket.startHandshake();
        Certificate[] certs = socket.getSession().getPeerCertificates();
        Certificate cert = certs[0];
        PublicKey key = cert.getPublicKey();
        System.out.println(key);
        return key;
    }
    public static void main(String[] args) throws Exception {
        System.out.println(getKey("bctcl-parasuram.bctchn.local", 8443));
    }

}

但是当我 运行 它时,我得到以下异常:

Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1937)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1478)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:212)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:957)
    at sun.security.ssl.Handshaker.process_record(Handshaker.java:892)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1050)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1363)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1391)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1375)
    at RetrievePublicKey.getKey(RetrievePublicKey.java:22)
    at RetrievePublicKey.main(RetrievePublicKey.java:30)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
    at sun.security.validator.Validator.validate(Validator.java:260)
    at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1460)
    ... 9 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:145)
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:131)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
    ... 15 more

您可以使用 SSLSocket 获取证书及其 public 密钥:

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.security.cert.Certificate;
...

    String hostname = "your.host";
    SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();        
    SSLSocket socket = (SSLSocket) factory.createSocket(hostname, 443);
    socket.startHandshake();
    Certificate[] certs = socket.getSession().getPeerCertificates();
    Certificate cert = certs[0];
    PublicKey key = cert.getPublicKey();

它仅在证书有效(不是自签名或由未知授权机构签名)时有效。对于自签名证书,您可以定义自己的 TrustManager 来信任所有内容。参见

但应尽可能避免,因为这种留下的代码会在以后造成安全问题。

当服务器被应用程序识别时获取证书可以使用 Zielu 的答案来完成。但是,如果服务器无法识别(例如,自签名或由不包含在 JVM 密钥库中的未知根权限签名),您可以使用 InstallCert 以编程方式检索服务器的证书。缩写版本如下: [警告:正如其他人所说,只有在您知道并信任证书所有者的情况下才能以这种方式使用不受信任的证书,否则会带来安全风险。]

import javax.net.ssl.*;
import java.io.*;
import java.security.*;
import java.security.cert.*;

public class FetchCert {

    public static void main(String[] args) throws Exception {
        //REPLACE THIS WITH YOUR TARGET HOST NAME
        String hostname = "example.com";
        SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();

        SSLSocket socket = (SSLSocket) factory.createSocket(hostname, 443);
        try {
            socket.startHandshake();
            socket.close();
            System.out.println("No errors, certificate is already trusted");
            return;
        } catch (SSLException e) {
            System.out.println("cert likely not found in keystore, will pull cert...");
        }


        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        char[] password = "changeit".toCharArray();
        ks.load(null, password);

        SSLContext context = SSLContext.getInstance("TLS");
        TrustManagerFactory tmf =
                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ks);
        X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
        SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
        context.init(null, new TrustManager[]{tm}, null);
        factory = context.getSocketFactory();

        socket = (SSLSocket) factory.createSocket(hostname, 443);
        try {
            socket.startHandshake();
        } catch (SSLException e) {
            //we should get to here
        }
        X509Certificate[] chain = tm.chain;
        if (chain == null) {
            System.out.println("Could not obtain server certificate chain");
            return;
        }

        X509Certificate cert = chain[0];
        String alias = hostname;
        ks.setCertificateEntry(alias, cert);

        System.out.println("saving file jssecacerts to working dir");
        System.out.println("copy this file to your jre/lib/security folder");
        FileOutputStream fos = new FileOutputStream("jssecacerts");
        ks.store(fos, password);
        fos.close();
    }
    private static class SavingTrustManager implements X509TrustManager {

        private final X509TrustManager tm;
        private X509Certificate[] chain;

        SavingTrustManager(X509TrustManager tm) {
            this.tm = tm;
        }

        public X509Certificate[] getAcceptedIssuers() {

        return new X509Certificate[0];  
        }

        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            throw new UnsupportedOperationException();
        }

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