簡體   English   中英

如何使用Java獲取安全網頁的公鑰

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

我需要通過java以編程方式獲取受保護網站的公共密鑰。 我已經讀過這個這個這個這個以及其他。 但是我還沒有找到通過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獲取證書及其公鑰:

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,它將信任所有內容。 請參閱允許Java使用不受信任的證書進行SSL / HTTPS連接

但是,如果可以的話,應該避免這樣做,因為稍后留下的這種代碼會帶來安全問題。

可以使用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);
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM