如何在 Java 密钥库中导入现有的 X.509 证书和私钥以在 SSL 中使用?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/906402/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 20:54:48  来源:igfitidea点击:

How to import an existing X.509 certificate and private key in Java keystore to use in SSL?

javassljmsactivemqjks

提问by Aleksandar Ivanisevic

I have this in an ActiveMQ config:

我在 ActiveMQ 配置中有这个:

<sslContext>
        <sslContext keyStore="file:/home/alex/work/amq/broker.ks"  
 keyStorePassword="password" trustStore="file:${activemq.base}/conf/broker.ts" 
 trustStorePassword="password"/>
</sslContext>

I have a pair of X.509 cert and a key file.

我有一对 X.509 证书和一个密钥文件。

How do I import those two in order to use them in SSL and SSL+stomp connectors? All examples I could google always generate the key themselves, but I already have a key.

如何导入这两个以便在 SSL 和 SSL+stomp 连接器中使用它们?我可以用谷歌搜索的所有示例总是自己生成密钥,但我已经有了一个密钥。

I have tried

我试过了

keytool -import  -keystore ./broker.ks -file mycert.crt

but this only imports the certificate and not the key file and results in

但这只会导入证书而不是密钥文件并导致

2009-05-25 13:16:24,270 [localhost:61612] ERROR TransportConnector - Could not accept connection : No available certificate or key corresponds to the SSL cipher suites which are enabled.

I have tried concatenating the cert and the key but got the same result.

我尝试连接证书和密钥,但得到了相同的结果。

How do I import the key?

如何导入密钥?

采纳答案by Matej

Believe or not, keytool does not provide such basic functionality like importing private key to keystore. You can try this workaroundwith merging PKSC12 file with private key to a keystore.

信不信由你,keytool 并没有提供像将私钥导入密钥库这样的基本功能。您可以通过将 PKSC12 文件与私钥合并到密钥库来尝试此解决方法

Or just use more user-friendly KeyManfrom IBM for keystore handling instead of keytool.exe.

或者只是使用IBM 提供的更加用户友好的KeyMan来处理密钥库,而不是使用 keytool.exe。

回答by Aleksandar Ivanisevic

Yes, it's indeed a sad fact that keytool has no functionality to import a private key.

是的,keytool 没有导入私钥的功能确实是一个可悲的事实。

For the record, at the end I went with the solution described here

为了记录,最后我使用了这里描述的解决方案

回答by CoverosGene

Keytool in Java 6 does have this capability: Importing private keys into a Java keystore using keytool

Java 6 中的 Keytool 确实具有此功能:使用 keytool 将私钥导入 Java 密钥库

Here are the basic details from that post.

以下是该帖子的基本细节。

  1. Convert the existing cert to a PKCS12 using OpenSSL. A password is required when asked or the 2nd step will complain.

    openssl pkcs12 -export -in [my_certificate.crt] -inkey [my_key.key] -out [keystore.p12] -name [new_alias] -CAfile [my_ca_bundle.crt] -caname root
    
  2. Convert the PKCS12 to a Java Keystore File.

    keytool -importkeystore -deststorepass [new_keystore_pass] -destkeypass [new_key_pass] -destkeystore [keystore.jks] -srckeystore [keystore.p12] -srcstoretype PKCS12 -srcstorepass [pass_used_in_p12_keystore] -alias [alias_used_in_p12_keystore]
    
  1. 使用 OpenSSL 将现有证书转换为 PKCS12。询问时需要密码,否则第 2 步会报错。

    openssl pkcs12 -export -in [my_certificate.crt] -inkey [my_key.key] -out [keystore.p12] -name [new_alias] -CAfile [my_ca_bundle.crt] -caname root
    
  2. 将 PKCS12 转换为 Java 密钥库文件。

    keytool -importkeystore -deststorepass [new_keystore_pass] -destkeypass [new_key_pass] -destkeystore [keystore.jks] -srckeystore [keystore.p12] -srcstoretype PKCS12 -srcstorepass [pass_used_in_p12_keystore] -alias [alias_used_in_p12_keystore]
    

回答by reto

I used the following two steps which I found in the comments/posts linked in the other answers:

我使用了以下两个步骤,我在其他答案中链接的评论/帖子中找到了这些步骤:

Step one: Convert the x.509 cert and key to a pkcs12 file

第一步:将 x.509 证书和密钥转换为 pkcs12 文件

openssl pkcs12 -export -in server.crt -inkey server.key \
               -out server.p12 -name [some-alias] \
               -CAfile ca.crt -caname root

Note:Make sure you put a password on the pkcs12 file - otherwise you'll get a null pointer exception when you try to import it. (In case anyone else had this headache). (Thanks jocull!)

注意:确保在 pkcs12 文件上设置了密码 - 否则在尝试导入时会出现空指针异常。(以防其他人有这种头痛)。(谢谢jocull!

Note 2:You might want to add the -chainoption to preserve the full certificate chain. (Thanks Mafuba)

注意 2:您可能希望添加-chain选项以保留完整的证书链。(感谢马福巴

Step two: Convert the pkcs12 file to a Java keystore

第二步:将 pkcs12 文件转换为 Java 密钥库

keytool -importkeystore \
        -deststorepass [changeit] -destkeypass [changeit] -destkeystore server.keystore \
        -srckeystore server.p12 -srcstoretype PKCS12 -srcstorepass some-password \
        -alias [some-alias]

Finished

完成的

OPTIONAL Step zero: Create self-signed certificate

可选步骤零:创建自签名证书

openssl genrsa -out server.key 2048
openssl req -new -out server.csr -key server.key
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

Cheers!

干杯!

回答by Interkot

In my case I had a pem file which contained two certificates and an encrypted private key to be used in mutual SSL authentication. So my pem file looked like this:

就我而言,我有一个 pem 文件,其中包含两个证书和一个用于相互 SSL 身份验证的加密私钥。所以我的 pem 文件看起来像这样:

-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,C8BF220FC76AA5F9
...
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

Here is what I did:

这是我所做的:

Split the file into three separate files, so that each one contains just one entry, starting with "---BEGIN.." and ending with "---END.." lines. Lets assume we now have three files: cert1.pem cert2.pem and pkey.pem

将文件拆分为三个单独的文件,使每个文件只包含一个条目,以“---BEGIN..”开头并以“---END..”行结尾。假设我们现在有三个文件:cert1.pem cert2.pem 和 pkey.pem

Convert pkey.pem into DER format using openssl and the following syntax:

使用 openssl 和以下语法将 pkey.pem 转换为 DER 格式:

openssl pkcs8 -topk8 -nocrypt -in pkey.pem -inform PEM -out pkey.der -outform DER

openssl pkcs8 -topk8 -nocrypt -in pkey.pem -inform PEM -out pkey.der -outform DER

Note, that if the private key is encrypted you need to supply a password( obtain it from the supplier of the original pem file ) to convert to DER format, openssl will ask you for the password like this: "enter a pass phraze for pkey.pem: " If conversion is successful, you will get a new file called "pkey.der"

请注意,如果私钥是加密的,您需要提供密码(从原始 pem 文件的供应商处获取)以转换为 DER 格式,openssl 将要求您提供这样的密码:“输入 pkey 的密码短语.pem: " 如果转换成功,你会得到一个名为 "pkey.der" 的新文件

Create a new java key store and import the private key and the certificates:

创建一个新的 java 密钥库并导入私钥和证书:

String keypass = "password";  // this is a new password, you need to come up with to protect your java key store file
String defaultalias = "importkey";
KeyStore ks = KeyStore.getInstance("JKS", "SUN");

// this section does not make much sense to me, 
// but I will leave it intact as this is how it was in the original example I found on internet:   
ks.load( null, keypass.toCharArray());
ks.store( new FileOutputStream ( "mykeystore"  ), keypass.toCharArray());
ks.load( new FileInputStream ( "mykeystore" ),    keypass.toCharArray());
// end of section..


// read the key file from disk and create a PrivateKey

FileInputStream fis = new FileInputStream("pkey.der");
DataInputStream dis = new DataInputStream(fis);
byte[] bytes = new byte[dis.available()];
dis.readFully(bytes);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);

byte[] key = new byte[bais.available()];
KeyFactory kf = KeyFactory.getInstance("RSA");
bais.read(key, 0, bais.available());
bais.close();

PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec ( key );
PrivateKey ff = kf.generatePrivate (keysp);


// read the certificates from the files and load them into the key store:

Collection  col_crt1 = CertificateFactory.getInstance("X509").generateCertificates(new FileInputStream("cert1.pem"));
Collection  col_crt2 = CertificateFactory.getInstance("X509").generateCertificates(new FileInputStream("cert2.pem"));

Certificate crt1 = (Certificate) col_crt1.iterator().next();
Certificate crt2 = (Certificate) col_crt2.iterator().next();
Certificate[] chain = new Certificate[] { crt1, crt2 };

String alias1 = ((X509Certificate) crt1).getSubjectX500Principal().getName();
String alias2 = ((X509Certificate) crt2).getSubjectX500Principal().getName();

ks.setCertificateEntry(alias1, crt1);
ks.setCertificateEntry(alias2, crt2);

// store the private key
ks.setKeyEntry(defaultalias, ff, keypass.toCharArray(), chain );

// save the key store to a file         
ks.store(new FileOutputStream ( "mykeystore" ),keypass.toCharArray());

(optional) Verify the content of your new key store:

(可选)验证新密钥库的内容:

keytool -list -keystore mykeystore -storepass password

keytool -list -keystore mykeystore -storepass password

Keystore type: JKS Keystore provider: SUN

Your keystore contains 3 entries

cn=...,ou=...,o=.., Sep 2, 2014, trustedCertEntry, Certificate fingerprint (SHA1): 2C:B8: ...

importkey, Sep 2, 2014, PrivateKeyEntry, Certificate fingerprint (SHA1): 9C:B0: ...

cn=...,o=...., Sep 2, 2014, trustedCertEntry, Certificate fingerprint (SHA1): 83:63: ...

密钥库类型:JKS 密钥库提供者:SUN

您的密钥库包含 3 个条目

cn=...,ou=...,o=.., 2014 年 9 月 2 日,trustedCertEntry,证书指纹 (SHA1): 2C:B8: ...

importkey, Sep 2, 2014, PrivateKeyEntry, 证书指纹 (SHA1): 9C:B0: ...

cn=...,o=...., 2014 年 9 月 2 日,trustedCertEntry,证书指纹 (SHA1):83:63:...

(optional) Test your certificates and private key from your new key store against your SSL server: ( You may want to enable debugging as an VM option: -Djavax.net.debug=all )

(可选)针对 SSL 服务器测试新密钥库中的证书和私钥:(您可能希望启用调试作为 VM 选项: -Djavax.net.debug=all )

        char[] passw = "password".toCharArray();
        KeyStore ks = KeyStore.getInstance("JKS", "SUN");
        ks.load(new FileInputStream ( "mykeystore" ), passw );

        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(ks, passw);

        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ks);
        TrustManager[] tm = tmf.getTrustManagers();

        SSLContext sclx = SSLContext.getInstance("TLS");
        sclx.init( kmf.getKeyManagers(), tm, null);

        SSLSocketFactory factory = sclx.getSocketFactory();
        SSLSocket socket = (SSLSocket) factory.createSocket( "192.168.1.111", 443 );
        socket.startHandshake();

        //if no exceptions are thrown in the startHandshake method, then everything is fine..

Finally register your certificates with HttpsURLConnection if plan to use it:

如果计划使用它,最后使用 HttpsURLConnection 注册您的证书:

        char[] passw = "password".toCharArray();
        KeyStore ks = KeyStore.getInstance("JKS", "SUN");
        ks.load(new FileInputStream ( "mykeystore" ), passw );

        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(ks, passw);

        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ks);
        TrustManager[] tm = tmf.getTrustManagers();

        SSLContext sclx = SSLContext.getInstance("TLS");
        sclx.init( kmf.getKeyManagers(), tm, null);

        HostnameVerifier hv = new HostnameVerifier()
        {
            public boolean verify(String urlHostName, SSLSession session)
            {
                if (!urlHostName.equalsIgnoreCase(session.getPeerHost()))
                {
                    System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
                }
                return true;
            }
        };

        HttpsURLConnection.setDefaultSSLSocketFactory( sclx.getSocketFactory() );
        HttpsURLConnection.setDefaultHostnameVerifier(hv);

回答by David Tonhofer

And one more:

还有一个:

#!/bin/bash

# We have:
#
# 1) $KEY : Secret key in PEM format ("-----BEGIN RSA PRIVATE KEY-----") 
# 2) $LEAFCERT : Certificate for secret key obtained from some
#    certification outfit, also in PEM format ("-----BEGIN CERTIFICATE-----")   
# 3) $CHAINCERT : Intermediate certificate linking $LEAFCERT to a trusted
#    Self-Signed Root CA Certificate 
#
# We want to create a fresh Java "keystore" $TARGET_KEYSTORE with the
# password $TARGET_STOREPW, to be used by Tomcat for HTTPS Connector.
#
# The keystore must contain: $KEY, $LEAFCERT, $CHAINCERT
# The Self-Signed Root CA Certificate is obtained by Tomcat from the
# JDK's truststore in /etc/pki/java/cacerts

# The non-APR HTTPS connector (APR uses OpenSSL-like configuration, much
# easier than this) in server.xml looks like this 
# (See: https://tomcat.apache.org/tomcat-6.0-doc/ssl-howto.html):
#
#  <Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
#                SSLEnabled="true"
#                maxThreads="150" scheme="https" secure="true"
#                clientAuth="false" sslProtocol="TLS"
#                keystoreFile="/etc/tomcat6/etl-web.keystore.jks"
#                keystorePass="changeit" />
#

# Let's roll:    

TARGET_KEYSTORE=/etc/tomcat6/foo-server.keystore.jks
TARGET_STOREPW=changeit

TLS=/etc/pki/tls

KEY=$TLS/private/httpd/foo-server.example.com.key
LEAFCERT=$TLS/certs/httpd/foo-server.example.com.pem
CHAINCERT=$TLS/certs/httpd/chain.cert.pem

# ----
# Create PKCS#12 file to import using keytool later
# ----

# From https://www.sslshopper.com/ssl-converter.html:
# The PKCS#12 or PFX format is a binary format for storing the server certificate,
# any intermediate certificates, and the private key in one encryptable file. PFX
# files usually have extensions such as .pfx and .p12. PFX files are typically used 
# on Windows machines to import and export certificates and private keys.

TMPPW=$$ # Some random password

PKCS12FILE=`mktemp`

if [[ $? != 0 ]]; then
  echo "Creation of temporary PKCS12 file failed -- exiting" >&2; exit 1
fi

TRANSITFILE=`mktemp`

if [[ $? != 0 ]]; then
  echo "Creation of temporary transit file failed -- exiting" >&2; exit 1
fi

cat "$KEY" "$LEAFCERT" > "$TRANSITFILE"

openssl pkcs12 -export -passout "pass:$TMPPW" -in "$TRANSITFILE" -name etl-web > "$PKCS12FILE"

/bin/rm "$TRANSITFILE"

# Print out result for fun! Bug in doc (I think): "-pass " arg does not work, need "-passin"

openssl pkcs12 -passin "pass:$TMPPW" -passout "pass:$TMPPW" -in "$PKCS12FILE" -info

# ----
# Import contents of PKCS12FILE into a Java keystore. WTF, Sun, what were you thinking?
# ----

if [[ -f "$TARGET_KEYSTORE" ]]; then
  /bin/rm "$TARGET_KEYSTORE"
fi

keytool -importkeystore \
   -deststorepass  "$TARGET_STOREPW" \
   -destkeypass    "$TARGET_STOREPW" \
   -destkeystore   "$TARGET_KEYSTORE" \
   -srckeystore    "$PKCS12FILE" \
   -srcstoretype  PKCS12 \
   -srcstorepass  "$TMPPW" \
   -alias foo-the-server

/bin/rm "$PKCS12FILE"

# ----
# Import the chain certificate. This works empirically, it is not at all clear from the doc whether this is correct
# ----

echo "Importing chain"

TT=-trustcacerts

keytool -import $TT -storepass "$TARGET_STOREPW" -file "$CHAINCERT" -keystore "$TARGET_KEYSTORE" -alias chain

# ----
# Print contents
# ----

echo "Listing result"

keytool -list -storepass "$TARGET_STOREPW" -keystore "$TARGET_KEYSTORE"

回答by Joshua Davies

Previous answers point out correctly that you can only do this with the standard JDK tools by converting the JKS file into PKCS #12 format first. If you're interested, I put together a compact utility to import OpenSSL-derived keys into a JKS-formatted keystore without having to convert the keystore to PKCS #12 first: http://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art049

以前的答案正确地指出,您只能通过首先将 JKS 文件转换为 PKCS #12 格式来使用标准 JDK 工具执行此操作。如果您有兴趣,我整理了一个紧凑的实用程序,可以将 OpenSSL 派生的密钥导入 JKS 格式的密钥库,而无需先将密钥库转换为 PKCS #12:http: //commandlinefanatic.com/cgi-bin/showarticle。 cgi?article=art049

You would use the linked utility like this:

您可以像这样使用链接的实用程序:

$ openssl req -x509 -newkey rsa:2048 -keyout localhost.key -out localhost.csr -subj "/CN=localhost"

(sign the CSR, get back localhost.cer)

(签署CSR,取回localhost.cer)

$ openssl rsa -in localhost.key -out localhost.rsa
Enter pass phrase for localhost.key:
writing RSA key
$ java -classpath . KeyImport -keyFile localhost.rsa -alias localhost -certificateFile localhost.cer -keystore localhost.jks -keystorePassword changeit -keystoreType JKS -keyPassword changeit

回答by vanval

You can use these steps to import the key to an existing keystore. The instructions are combined from answers in this thread and other sites. These instructions worked for me (the java keystore):

您可以使用这些步骤将密钥导入现有的密钥库。这些说明结合了本线程和其他站点中的答案。这些说明对我有用(Java 密钥库):

  1. Run

openssl pkcs12 -export -in yourserver.crt -inkey yourkey.key -out server.p12 -name somename -certfile yourca.crt -caname root

openssl pkcs12 -export -in yourserver.crt -inkey yourkey.key -out server.p12 -name somename -certfile yourca.crt -caname root

(If required put the -chain option. Putting that failed for me). This will ask for the password - you must give the correct password else you will get an error (heading error or padding error etc).

(如果需要,放置 -chain 选项。放置对我来说失败了)。这将要求输入密码 - 您必须提供正确的密码,否则您将收到错误(标题错误或填充错误等)。

  1. It will ask you to enter a new password - you must enter a password here - enter anything but remember it. (Let us assume you enter Aragorn).
  2. This will create the server.p12 file in the pkcs format.
  3. Now to import it into the *.jksfile run:
    keytool -importkeystore -srckeystore server.p12 -srcstoretype PKCS12 -destkeystore yourexistingjavakeystore.jks -deststoretype JKS -deststorepass existingjavastorepassword -destkeypass existingjavastorepassword
    (Very important - do not leave out the deststorepass and the destkeypass parameters.)
  4. It will ask you for the src key store password. Enter Aragorn and hit enter. The certificate and key is now imported into your existing java keystore.
  1. 它会要求您输入新密码 - 您必须在此处输入密码 - 输入任何内容,但记住它。(让我们假设您进入 Aragorn)。
  2. 这将创建 pkcs 格式的 server.p12 文件。
  3. 现在将它导入到*.jks文件中:(
    keytool -importkeystore -srckeystore server.p12 -srcstoretype PKCS12 -destkeystore yourexistingjavakeystore.jks -deststoretype JKS -deststorepass existingjavastorepassword -destkeypass existingjavastorepassword
    非常重要 - 不要遗漏 deststorepass 和 destkeypass 参数。)
  4. 它会询问您 src 密钥库密码。输入阿拉贡并按回车键。证书和密钥现在已导入到您现有的 Java 密钥库中。

回答by Alex Fotios

Based on the answers above, here is how to create a brand new keystore for your java based web server, out of an independently created Comodo cert and private key using keytool (requires JDK 1.6+)

根据上面的答案,这里是如何使用 keytool 从独立创建的 Comodo 证书和私钥中为基于 Java 的 Web 服务器创建全新的密钥库(需要 JDK 1.6+)

  1. Issue this command and at the password prompt enter somepass - 'server.crt' is your server's cert and 'server.key' is the private key you used for issuing the CSR:openssl pkcs12 -export -in server.crt -inkey server.key -out server.p12 -name www.yourdomain.com -CAfile AddTrustExternalCARoot.crt -caname "AddTrust External CA Root"

  2. Then use keytool to convert the p12 keystore into a jks keystore:keytool -importkeystore -deststorepass somepass -destkeypass somepass -destkeystore keystore.jks -srckeystore server.p12 -srcstoretype PKCS12 -srcstorepass somepass

  1. 发出此命令并在密码提示下输入 somepass - 'server.crt' 是您服务器的证书,'server.key' 是您用于发布 CSR 的私钥:openssl pkcs12 -export -in server.crt -inkey server.key -out server.p12 -name www.yourdomain.com -CAfile AddTrustExternalCARoot.crt -caname "AddTrust External CA Root"

  2. 然后使用keytool将p12 keystore转换成jks keystore:keytool -importkeystore -deststorepass somepass -destkeypass somepass -destkeystore keystore.jks -srckeystore server.p12 -srcstoretype PKCS12 -srcstorepass somepass

Then import the other two root/intermediate certs you received from Comodo:

然后导入您从 Comodo 收到的其他两个根/中间证书:

  1. Import COMODORSAAddTrustCA.crt:keytool -import -trustcacerts -alias cert1 -file COMODORSAAddTrustCA.crt -keystore keystore.jks

  2. Import COMODORSADomainValidationSecureServerCA.crt:keytool -import -trustcacerts -alias cert2 -file COMODORSADomainValidationSecureServerCA.crt -keystore keystore.jks

  1. 导入 COMODORSAAddTrustCA.crt:keytool -import -trustcacerts -alias cert1 -file COMODORSAAddTrustCA.crt -keystore keystore.jks

  2. 导入 COMODORSADomainValidationSecureServerCA.crt:keytool -import -trustcacerts -alias cert2 -file COMODORSADomainValidationSecureServerCA.crt -keystore keystore.jks

回答by Micha? Jurczuk

First convert to p12:

首先转换为p12:

openssl pkcs12 -export -in [filename-certificate] -inkey [filename-key] -name [host] -out [filename-new-PKCS-12.p12]

Create new JKS from p12:

从 p12 创建新的 JKS:

keytool -importkeystore -deststorepass [password] -destkeystore [filename-new-keystore.jks] -srckeystore [filename-new-PKCS-12.p12] -srcstoretype PKCS12