java - Java 和 HTTPS 中的导入私钥问题

标签 java authentication ssl client

Java 的 keytool 不提供从私钥创建新 keystore 或将私钥导入现有 jks 存储的工具。我有一个服务主机,要求他们生成私钥并将其提供给我们(客户端)以调用他们的服务。

私钥和证书在 firefox 中从他们那里下载,在 windows 机器上导出为 p12 格式的文件。我使用 openssl 将该 pkcs12 转换为 PEM 文件。然后我将该文件分别手动拆分为 cert.der 和 privateKey.der 文件。然后我编写了一个 java 程序将这对导入我们现有的 keystore (使用充气城堡)。运行程序后,我使用 keytool 验证 keystore 已创建并包含我的 key /证书,它看起来不错。

运行时得到的程序代码和错误信息如下。我还注意到当我使用 keytool -printcert 时的指纹签名与 firefox 在其“查看证书”菜单中显示的不同,这是一个危险信号吗?

非常感谢您对此处注意事项和/或想法的任何帮助。

private static InputStream fullStream(String fname) throws IOException {
        File f = new File(fname);
        if (f == null || f.exists() == false) {
            System.out.println("File " + fname + " does not exist");
            System.exit(1);
        }
        FileInputStream fis = new FileInputStream(f);
        DataInputStream dis = new DataInputStream(fis);
        byte[] bytes = new byte[dis.available()];
        dis.readFully(bytes);
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        return bais;
    }


    public static void main(String args[]) {
        try {
            if (args.length < 6) {
                System.out
                        .println("\nImportPrivateKeyTool Usage: \njava ImportPrivateKeyTool parameters:\n"
                                + "\n<keystoreFileName> - JKS format\n"
                                + "\n<keystorePassword>\n"
                                + "\n<keyFileName> - PKCS12 format \n"
                                + "\n<keyPwd>\n"
                                + "\n<keyAlias>\n");
                System.out
                        .println("\n\nRequires jsse for PKCS12 keystore support \n"
                                + " - source storetype can be JKS or PKCS12\n"
                                + " - destination storetype must be JKS type (PKCS12 write not supported)\n");
                System.exit(1);
            }

            String keystoreFileName = args[0];
            String keystorePassword = args[1];
            String keyFileName = args[2];
            String keyPwd = args[3];
            String keyAlias = args[4];
            String certFileName = args[5];

            System.setProperty("javax.net.ssl.keyStore", keystoreFileName);// ie
                                                                            // "C:/Dev/security/keystores/.deluxeKeyStore.jks"
            System.setProperty("javax.net.ssl.keyStorePassword",
                    keystorePassword);

            Security.addProvider(new BouncyCastleProvider());

            // initializing keystore , clear it first by passing null??
            KeyStore ks = KeyStore.getInstance("JKS");// second param SUN?
                                                        // "SUN", //TODO allow
                                                        // passing of PKCS12 or
                                                        // JKS?

            File f = new File(keystoreFileName);
            if (f == null || f.exists() == false) {
                //create new
                ks.load(null, keystorePassword.toCharArray());//initialize
                ks.store(new FileOutputStream(keystoreFileName),keystorePassword.toCharArray());
                System.out.println("Keystore file " + keystoreFileName + " did not exist so created new key store.");
            }

            ks.load(fullStream(keystoreFileName),keystorePassword.toCharArray());
            System.out.println("Using keystore-file : " + keystoreFileName);



            KeyFactory kf = KeyFactory.getInstance("RSA");

            BufferedReader br = new BufferedReader(new FileReader(keyFileName));
            PEMReader privateKeyPEMReader = new PEMReader(br);
            KeyPair kp = (KeyPair) privateKeyPEMReader.readObject();
            PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded()); 
            PrivateKey ff = kf.generatePrivate(keysp);//keysp
            System.out.println("Successfully loaded into memory private key: "+ keyFileName);

            // loading CertificateChain
            CertificateFactory cf = CertificateFactory.getInstance("X.509");of
            BufferedReader br2 = new BufferedReader(new FileReader(certFileName));// cert?
            //InputStream certstream = fullStream(certFileName);
            PEMReader certPEMReader = new PEMReader(br2);
            Certificate cert = (Certificate)certPEMReader.readObject(); //TODO support chain array of certs..
            Certificate[] certs = new Certificate[1];
            certs[0] = cert;


            // storing keystore
            ks.setKeyEntry(keyAlias, ff, keyPwd.toCharArray(), certs);
            System.out
                    .println("Key and certificate successfully imported as alias:"
                            + keyAlias);
            ks.store(new FileOutputStream(keystoreFileName),
                    keystorePassword.toCharArray()); // TODO use key pass
                                                        // instead? doubt it
            System.out.println("Successfully saved updated key store.");







ClientKeyExchange, RSA PreMasterSecret, TLSv1
main, WRITE: TLSv1 Handshake, length = 876
SESSION KEYGEN:
PreMaster Secret:
<removed>....
CONNECTION KEYGEN:
Client Nonce:
<remove>.....
Server Nonce:
<remove>
Master Secret:
<removed>
Client MAC write Secret:
<removed>
Server MAC write Secret:
<removed>
Client write key:
<removed>
Server write key:
<removed>
... no IV used for this cipher
*** CertificateVerify
main, WRITE: TLSv1 Handshake, length = 262
main, WRITE: TLSv1 Change Cipher Spec, length = 1
*** Finished
verify_data:  { 20, 221, 183, 152, 78, 193, 208, 28, 198, 116, 172, 58 }
***
main, WRITE: TLSv1 Handshake, length = 32
main, READ: TLSv1 Alert, length = 2
main, RECV TLSv1 ALERT:  fatal, decrypt_error
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: decrypt_error
javax.net.ssl.SSLHandshakeException: Received fatal alert: decrypt_error
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1720)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:954)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1138)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1165)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1149)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:434)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1172)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:234)
    at com.oflows.seam.test.deluxechecks.DeluxeChecksOrderProTest.main(DeluxeChecksOrderProTest.java:84)

最佳答案

我不确定我是否理解最终目标是什么......

  • 如果您想使用 PKCS#12 文件中已有的证书 + 私钥,无需转换:使用 Java 中的 PKCS12 keystore 类型。您没有显示正在使用的客户端代码,但其中一种使用方法是将 javax.net.ssl.keyStoreType 设置为 PKCS12

  • keytool,自 Java 6 以来由 Sun/Oracle 提供,可以将证书+私钥对从一个 keystore (包括 PKCS#12)导入另一个 keystore ,例如:

    keytool -importkeystore -srckeystore thekeystore.p12 \
                    -srcstoretype PKCS12 \
                    -destkeystore thekeystore.jks \
                    -deststoretype JKS
    

这些选项不需要 BouncyCaSTLe(也不需要通过 OpenSSL 导出)。

关于java - Java 和 HTTPS 中的导入私钥问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8392366/

相关文章:

php - 像 Google Identity Toolkit 这样的简单 SSO 系统,但没有锁定的 UI?

Android 服务器响应特殊字符 ��������������V*��I

linux - 无法将 docker 镜像推/拉到经过证书身份验证的私有(private)注册表? (仅适用于 WSL,远程错误 : tls: alert(116))

java - Whatsapp 共享在 Webview 中不起作用

java - 使用java处理多个csv行

java - Java War文件在jar文件中引发FileNotFoundException

python - 无法通过 python 中的公司代理将正确的凭据传递到服务器,但可以在 R 中执行此操作

java - JBoss中如何知道某个模块依赖于哪些模块?

Python Dash Basic Auth - 在应用程序中获取用户名

asp.net-mvc - 如何仅为登录和特定页面 MVC 激活 SSL