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

发布于 2024-07-22 02:34:50 字数 248 浏览 6 评论 0原文

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

如何将这两个导入到单个密钥库中? 我可以谷歌的所有例子总是自己生成密钥,但我已经有了密钥。

我尝试过:

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

但是,这仅导入证书而不导入密钥文件。 我尝试连接证书和密钥,但得到了相同的结果。

如何导入密钥?

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

How do I import those two in a single keystore? All examples I could Google always generate the key themselves, but I already have a key.

I have tried:

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

However, this only imports the certificate and not the key file. I have tried concatenating the cert and the key but got the same result.

How do I import the key?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

我一向站在原地 2024-07-29 02:34:50

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

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

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

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

注意 2: 您可能需要添加 -chain 选项来保留完整的证书链。 (感谢 Mafuba

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

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

完成

可选步骤零:创建自签名证书< /strong>

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

常见问题解答:我收到错误IOException:密钥库密码不正确

如果您使用 OpenSSL 3.0 和比 Java8u302 更新的 JDK 并收到以下错误:

keytool error: java.io.IOException: keystore password was incorrect

您可能陷入更改 openssl 中的默认密码。 此 Stack Overflow 答案 提供了答案。 也许可以通过投票来感谢托马斯。

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

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!)

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

Step two: Convert the pkcs12 file to a Java keystore

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

FAQ: I get error IOException: keystore password was incorrect

If you are using OpenSSL 3.0 and a JDK newer than Java8u302 and get the following error:

keytool error: java.io.IOException: keystore password was incorrect

You might caught in a change pf default cypher within openssl. This Stack Overflow Answer provides an answer. Maybe thank Thomas with an upvote.

三生殊途 2024-07-29 02:34:50

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

以下是该帖子的基本详细信息。

  1. 使用 OpenSSL 将现有证书转换为 PKCS12。 询问时需要输入密码,否则第二步会出错。

    openssl pkcs12 -export -in [my_certificate.crt] -inkey [my_key.key] \ 
        -out [keystore.p12] -name [new_alias] -CAfile [my_ca_bundle.crt] \ 
        -caname根 
      
  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] 
      

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

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]
    
以酷 2024-07-29 02:34:50

不管你信不信,keytool 不提供像将私钥导入密钥库这样的基本功能。 您可以尝试这个解决方法 将带有私钥的 PKSC12 文件合并到密钥库:

keytool -importkeystore \
  -deststorepass storepassword \
  -destkeypass keypassword \
  -destkeystore my-keystore.jks \
  -srckeystore cert-and-key.p12 \
  -srcstoretype PKCS12 \
  -srcstorepass p12password \
  -alias 1

或者只是使用更用户友好的 KeyMan 用于密钥库处理,而不是 keytool。

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

keytool -importkeystore \
  -deststorepass storepassword \
  -destkeypass keypassword \
  -destkeystore my-keystore.jks \
  -srckeystore cert-and-key.p12 \
  -srcstoretype PKCS12 \
  -srcstorepass p12password \
  -alias 1

Or just use more user-friendly KeyMan from IBM for keystore handling instead of keytool.

硬不硬你别怂 2024-07-29 02:34:50

使用 Let's Encrypt 证书

假设您已使用 Let's Encrypt/etc/letscrypt 中创建了证书和私钥/live/you.com

1. 创建一个 PKCS #12

openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out pkcs.p12 \
        -name letsencrypt

文件将您的 SSL 证书 fullchain.pem 和私钥 privkey.pem 合并到一个文件 pkcs.p12 中。

系统会提示您输入 pkcs.p12 的密码。

export 选项指定将创建而不是解析 PKCS #12 文件(根据 手册)。

2. 创建 Java 密钥库

keytool -importkeystore -destkeystore keystore.jks -srckeystore pkcs.p12 \
        -srcstoretype PKCS12 -alias letsencrypt

如果 keystore.jks 不存在,则会创建包含上一步中的 pkcs.12 的密钥库。 否则,您需要将 pkcs.12 导入到现有密钥库中。


这些说明源自 博客文章“从 Let's Encrypt 证书创建 Java 密钥库 (.JKS)”,作者:Maximilian Böhm。

这里有更多关于<中不同类型的文件代码>/etc/letsencrypt/live/you.com/。

Using Let's Encrypt certificates

Assuming you've created your certificates and private keys with Let's Encrypt in /etc/letsencrypt/live/you.com:

1. Create a PKCS #12 file

openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out pkcs.p12 \
        -name letsencrypt

This combines your SSL certificate fullchain.pem and your private key privkey.pem into a single file, pkcs.p12.

You'll be prompted for a password for pkcs.p12.

The export option specifies that a PKCS #12 file will be created rather than parsed (according to the manual).

2. Create the Java keystore

keytool -importkeystore -destkeystore keystore.jks -srckeystore pkcs.p12 \
        -srcstoretype PKCS12 -alias letsencrypt

If keystore.jks doesn't exist, it will be created containing the pkcs.12 from the previous step. Otherwise, you'll import pkcs.12 into the existing keystore.


These instructions are derived from the blog post "Create a Java Keystore (.JKS) from Let's Encrypt Certificates" by Maximilian Böhm.

Here's more on the different kind of files in /etc/letsencrypt/live/you.com/.

烟柳画桥 2024-07-29 02:34:50

首先转换为 p12:

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

从 p12 创建新的 JKS:

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

First convert to p12:

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

Create new JKS from p12:

keytool -importkeystore -deststorepass [password] -destkeystore [filename-new-keystore.jks] -srckeystore [filename-new-PKCS-12.p12] -srcstoretype PKCS12
和影子一齐双人舞 2024-07-29 02:34:50

还有一件事:

#!/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"

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"
a√萤火虫的光℡ 2024-07-29 02:34:50

就我而言,我有一个 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-----

这就是我所做的:

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

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

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

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

创建一个新的 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());

可选)验证新密钥存储的内容:

keytool -list -keystore mykeystore -storepass 密码

密钥库类型:JKS 密钥库提供程序:SUN

您的密钥库包含 3 个条目

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

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

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

(可选)针对 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..

最后,如果计划使用 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);

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:

-----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

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

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"

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

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

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: ...

(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 )

        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:

        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);
何以笙箫默 2024-07-29 02:34:50

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

作为记录,最后我采用了描述的解决方案 此处

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

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

自由如风 2024-07-29 02:34:50

我试图实现的是使用已经提供的私钥和证书来签署消息,该消息将发送到需要确保消息来自我的地方(私钥签名,而公钥加密)。

那么,如果您已经有 .key 文件和 .crt 文件呢?

试试这个:

第 1 步:将密钥和证书转换为 .p12 文件

openssl pkcs12 -export -in certificate.crt -inkey privateKey.key -name alias -out yourconvertedfile.p12

第 2 步:使用单个命令导入密钥并创建 .jsk 文件

keytool -importkeystore -deststorepass changeit -destkeystore keystore.jks -srckeystore umeme.p12 -srcstoretype PKCS12

第 3 步: 在您的 Java 中:

char[] keyPassword = "changeit".toCharArray();

KeyStore keyStore = KeyStore.getInstance("JKS");
InputStream keyStoreData = new FileInputStream("keystore.jks");

keyStore.load(keyStoreData, keyPassword);
KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(keyPassword);
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry("alias", entryPassword);

System.out.println(privateKeyEntry.toString());

如果您需要使用此密钥对某些字符串进行签名,请执行以下操作:

步骤 1:转换要加密的文本

byte[] data = "test".getBytes("UTF8");

步骤 2:获取 Base64 编码的私钥

keyStore.load(keyStoreData, keyPassword);

//get cert, pubkey and private key from the store by alias
Certificate cert = keyStore.getCertificate("localhost");
PublicKey publicKey = cert.getPublicKey();
KeyPair keyPair = new KeyPair(publicKey, (PrivateKey) key);

//sign with this alg
Signature sig = Signature.getInstance("SHA1WithRSA");
sig.initSign(keyPair.getPrivate());
sig.update(data);
byte[] signatureBytes = sig.sign();
System.out.println("Signature:" + Base64.getEncoder().encodeToString(signatureBytes));

sig.initVerify(keyPair.getPublic());
sig.update(data);

System.out.println(sig.verify(signatureBytes));

参考:

  1. < a href="https://stackoverflow.com/questions/906402/how-to-import-an-existing-x509-certificate-and-private-key-in-java-keystore-to-u">如何导入Java 密钥库中现有的 x509 证书和私钥要在 SSL 中使用吗?
  2. http: //tutorials.jenkov.com/java-cryptography/keystore.html
  3. http://www.java2s.com/Code/Java/Security/RetrivingaKeyPairfromaKeyStore.htm
  4. 如何用私钥对字符串进行签名

最终程序

public static void main(String[] args) throws Exception {

    byte[] data = "test".getBytes("UTF8");

    // load keystore
    char[] keyPassword = "changeit".toCharArray();

    KeyStore keyStore = KeyStore.getInstance("JKS");
    //System.getProperty("user.dir") + "" < for a file in particular path 
    InputStream keyStoreData = new FileInputStream("keystore.jks");
    keyStore.load(keyStoreData, keyPassword);

    Key key = keyStore.getKey("localhost", keyPassword);

    Certificate cert = keyStore.getCertificate("localhost");

    PublicKey publicKey = cert.getPublicKey();

    KeyPair keyPair = new KeyPair(publicKey, (PrivateKey) key);

    Signature sig = Signature.getInstance("SHA1WithRSA");

    sig.initSign(keyPair.getPrivate());
    sig.update(data);
    byte[] signatureBytes = sig.sign();
    System.out.println("Signature:" + Base64.getEncoder().encodeToString(signatureBytes));

    sig.initVerify(keyPair.getPublic());
    sig.update(data);

    System.out.println(sig.verify(signatureBytes));
}

What I was trying to achieve was using already provided private key and certificate to sign message that was going someplace that needed to make sure that the message was coming from me (private keys sign while public keys encrypt).

So if you already have a .key file and a .crt file?

Try this:

Step1: Convert the key and cert to .p12 file

openssl pkcs12 -export -in certificate.crt -inkey privateKey.key -name alias -out yourconvertedfile.p12

Step 2: Import the key and create a .jsk file with a single command

keytool -importkeystore -deststorepass changeit -destkeystore keystore.jks -srckeystore umeme.p12 -srcstoretype PKCS12

Step 3: In your java:

char[] keyPassword = "changeit".toCharArray();

KeyStore keyStore = KeyStore.getInstance("JKS");
InputStream keyStoreData = new FileInputStream("keystore.jks");

keyStore.load(keyStoreData, keyPassword);
KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(keyPassword);
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry("alias", entryPassword);

System.out.println(privateKeyEntry.toString());

If you need to sign some string using this key do the following:

Step 1: Convert the text you want to encrypt

byte[] data = "test".getBytes("UTF8");

Step 2: Get base64 encoded private key

keyStore.load(keyStoreData, keyPassword);

//get cert, pubkey and private key from the store by alias
Certificate cert = keyStore.getCertificate("localhost");
PublicKey publicKey = cert.getPublicKey();
KeyPair keyPair = new KeyPair(publicKey, (PrivateKey) key);

//sign with this alg
Signature sig = Signature.getInstance("SHA1WithRSA");
sig.initSign(keyPair.getPrivate());
sig.update(data);
byte[] signatureBytes = sig.sign();
System.out.println("Signature:" + Base64.getEncoder().encodeToString(signatureBytes));

sig.initVerify(keyPair.getPublic());
sig.update(data);

System.out.println(sig.verify(signatureBytes));

References:

  1. How to import an existing x509 certificate and private key in Java keystore to use in SSL?
  2. http://tutorials.jenkov.com/java-cryptography/keystore.html
  3. http://www.java2s.com/Code/Java/Security/RetrievingaKeyPairfromaKeyStore.htm
  4. How to sign string with private key

Final program

public static void main(String[] args) throws Exception {

    byte[] data = "test".getBytes("UTF8");

    // load keystore
    char[] keyPassword = "changeit".toCharArray();

    KeyStore keyStore = KeyStore.getInstance("JKS");
    //System.getProperty("user.dir") + "" < for a file in particular path 
    InputStream keyStoreData = new FileInputStream("keystore.jks");
    keyStore.load(keyStoreData, keyPassword);

    Key key = keyStore.getKey("localhost", keyPassword);

    Certificate cert = keyStore.getCertificate("localhost");

    PublicKey publicKey = cert.getPublicKey();

    KeyPair keyPair = new KeyPair(publicKey, (PrivateKey) key);

    Signature sig = Signature.getInstance("SHA1WithRSA");

    sig.initSign(keyPair.getPrivate());
    sig.update(data);
    byte[] signatureBytes = sig.sign();
    System.out.println("Signature:" + Base64.getEncoder().encodeToString(signatureBytes));

    sig.initVerify(keyPair.getPublic());
    sig.update(data);

    System.out.println(sig.verify(signatureBytes));
}
多情癖 2024-07-29 02:34:50

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

  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 "AddTrustExternalCA Root"

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

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

  1. Import 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

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+)

  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

Then import the other two root/intermediate certs you received from 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

眼中杀气 2024-07-29 02:34:50

您可以使用这些步骤将密钥导入到现有密钥库。 这些说明是根据该线程和其他站点中的答案组合而成的。 这些说明对我有用(java密钥库):

  1. 运行

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

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

  1. 它将要求您输入新密码 - 您必须在此处输入密码 - 输入任何内容但记住它。 (假设您进入阿拉贡)。
  2. 这将创建 pkcs 格式的 server.p12 文件。
  3. 现在将其导入到 *.jks 文件中,运行:
    <代码>
    keytool -importkeystore -srckeystore server.p12 -srcstoretype PKCS12
    -destkeystore yourexistingjavakeystore.jks -deststoretype JKS -deststorepass 现有javastore密码 -destkeypass 现有javastore密码

    (非常重要 - 不要遗漏 deststorepass 和 destkeypass 参数。)
  4. 它会询问您 src 密钥存储密码。 输入阿拉贡并按回车键。
    证书和密钥现已导入到您现有的 java 密钥库中。

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):

  1. Run

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).

  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 *.jks file 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.
笨笨の傻瓜 2024-07-29 02:34:50

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

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

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

:(签署 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

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

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)

$ 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
落在眉间の轻吻 2024-07-29 02:34:50

如果您有一个 PEM 文件(例如 server.pem),其中包含:

  • 受信任的证书
  • 私钥,

那么您可以将证书和密钥导入 JKS 密钥库,如下所示:

1 ) 将私钥从 PEM 文件复制到 ascii 文件(例如 server.key

2) 将证书从 PEM 文件复制到 ascii 文件(例如 server.crt)

3) 将证书和密钥导出到 PKCS12 文件中:

$ openssl pkcs12 -export -in server.crt -inkey server.key \
                 -out server.p12 -name [some-alias] -CAfile server.pem -caname root
  • PEM 文件可以用作 -CAfile 的参数 选项
  • 系统会提示您输入“导出”密码。
  • 如果在 git bash 中执行此操作,则将 winpty 添加到命令的开头,以便可以输入导出密码。

4) 将 PKCS12 文件转换为 JKS 密钥库:

$ keytool -importkeystore -deststorepass changeit -destkeypass changeit \
          -destkeystore keystore.jks  -srckeystore server.p12 -srcstoretype PKCS12 \
          -srcstorepass changeit
  • srcstorepass 密码应与步骤 3 中的导出密码匹配)

If you have a PEM file (e.g. server.pem) containing:

  • the trusted certificate
  • the private key

then you can import the certificate and key into a JKS keystore like this:

1) Copy the private key from the PEM file into an ascii file (e.g. server.key)

2) Copy the cert from the PEM file into an ascii file (e.g. server.crt)

3) Export the cert and key into a PKCS12 file:

$ openssl pkcs12 -export -in server.crt -inkey server.key \
                 -out server.p12 -name [some-alias] -CAfile server.pem -caname root
  • the PEM file can be used as the argument to the -CAfile option.
  • you are prompted for an 'export' password.
  • if doing this in git bash then add winpty to the start of the command so the export password can be entered.

4) Convert the PKCS12 file to a JKS keystore:

$ keytool -importkeystore -deststorepass changeit -destkeypass changeit \
          -destkeystore keystore.jks  -srckeystore server.p12 -srcstoretype PKCS12 \
          -srcstorepass changeit
  • the srcstorepass password should match the export password from step 3)
歌入人心 2024-07-29 02:34:50

只需制作一个PKCS12密钥库,Java现在就可以直接使用它。 事实上,如果您列出 Java 风格的密钥库,keytool 本身会提醒您 PKCS12 现在是首选格式。

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

您应该已从证书提供商处收到所有三个文件(server.crt、server.key、c​​a.crt)。 我不确定“-caname root”实际上是什么意思,但似乎必须以这种方式指定。

在 Java 代码中,确保指定正确的密钥库类型。

KeyStore.getInstance("PKCS12")

我通过这种方式让 comodo.com 颁发的 SSL 证书在 NanoHTTPD 中正常工作。

Just make a PKCS12 keystore, Java can use it directly now. In fact, if you list a Java-style keystore, keytool itself alerts you to the fact that PKCS12 is now the preferred format.

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

You should have received all three files (server.crt, server.key, ca.crt) from your certificate provider. I am not sure what "-caname root" actually means, but it seems to have to be specified that way.

In the Java code, make sure to specify the right keystore type.

KeyStore.getInstance("PKCS12")

I got my comodo.com-issued SSL certificate working fine in NanoHTTPD this way.

尬尬 2024-07-29 02:34:50

在椭圆曲线的情况下并回答问题在 Java 密钥库中导入现有的 x509 证书和私钥,您可能还想看看这个线程 如何在java中读取.pem文件格式的EC私钥

in a case of Elliptic Curve and answer the question import an existing x509 certificate and private key in Java keystore, you may want to have a look also to this thread How to read EC Private key in java which is in .pem file format

抚笙 2024-07-29 02:34:50

如果您在单个 .pem 文件中收到组合的证书和密钥(例如 MongoDB Atlas 的身份验证),则

使用文本编辑器打开 pem 文件并将它们拆分为两个文件,例如 < code>cert.pem 和 key.pem (在哪里可以进行分割,文件中写得很清楚),然后使用 openssl 命令创建一个像这样的单个 p12 格式文件:

 openssl pkcs12 -export -out server.p12 -name test\
 -in cert.pem -inkey key.pem

我正在使用 Java 8,事实证明至少在 Java 8 或更高版本中,生成的 p12 (server.p12) 现在是密钥库文件,因此您可以直接使用它如果您不需要向其中添加更多证书,则无需使用keytool

If you have received a combined cert and key in a single .pem file, like the MongoDB Atlas' authentication, then,

Open the pem file with a text editor and split them into two files, for example cert.pem and key.pem (where you can make a split is very clear in the file) and then use the openssl command to create a single p12 format file like this:

 openssl pkcs12 -export -out server.p12 -name test\
 -in cert.pem -inkey key.pem

I am using Java 8 and as it turns out at least in Java 8 or later the resulting p12 (server.p12) is now the keystore file so you can use it directly without a need to use the keytool if you do not need to add any more certs to it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文