• SpringBoot结合keytool配置ssl双向认证通信



    【关键词】:keytool、SpringBoot、restTemplate、ssl、双向认证、https、keystore、jks

    一、需求

    各对等机构各自部署一套服务平台(称作节点peer),要求在各机构平台之间内部通信使用ssl加密。支持自签名方式或ca认证方式。

    二、环境

    win10,jdk1.8.0_261。

    三、技术储备

    • jdk自带的keytool命令;
    • server.ssl服务端配置;
    • restTemplate客户端集成ssl配置;
    • 支持https的同时也支持http,便于调测;
    • 打开ssl握手日志-Djavax.net.debug=ssl:handshake,便于分析ssl。

    四、项目实现和测试

    4.0、大体思路

    1. 首先准备一个简单的SpringBoot项目,peer1写个/api/doSomething接口,内部再用restTemplate调用peer2的https接口https://peer2/interact/reply/hello
    2. keytool为peer1和peer2分别生成秘钥库以及信任秘钥库,并配置进ssl服务端和客户端;
    3. 同时开启http端口,便于postman直接调用peer1的http接口http://peer1/api/doSomething
    4. 测试自签名的peer1和peer2互认证书,以及同一CA签发的peer1和peer2证书,是否可以调通。

    4.1、项目准备

    完整项目点这里:
    https://github.com/oaHeZgnoS/ssl-peer

    • http外部接口:com.szh.peer.ctrl.SystemCtrl.doSomething()
    • https内部通信接口:com.szh.peer.ctrl.InteractCtrl.replyHello()

    4.2、keytool生成证书并配置

    4.2.1、自签名peer1/peer2

    peer1和peer2两个机构各自生成自己秘钥库,并将对方配置在自己的信任秘钥库中,从而达到互认互信加密通信。信任秘钥库可以合并在秘钥库,也可以是独立于秘钥库的另一个文件中。

    4.2.1.1、信任密钥库合并在密钥库
    ## 信任密钥库合并在密钥库
    
    一、peer1生成密钥库以及导出公钥证书
    1、生成peer1的密钥库peer1.jks
    keytool -genkeypair -alias peer1 -keystore peer1.jks -storepass passwd1 -dname CN=peer1,OU=peer1,O=peer1,L=peer1,C=CN
    2、查看密钥库详情
    keytool -list -keystore peer1.jks -storepass passwd1 -v
    3、peer1导出公钥证书
    keytool -export -alias peer1 -file peer1.cer -keystore peer1.jks -storepass passwd1
    
    二、peer2生成密钥库以及导出公钥证书
    1、生成peer2的密钥库peer2.jks
    keytool -genkeypair -alias peer2 -keystore peer2.jks -storepass passwd2 -dname CN=peer2,OU=peer2,O=peer2,L=peer2,C=CN
    2、查看密钥库详情
    keytool -list -keystore peer2.jks -storepass passwd2 -v
    3、peer2导出公钥证书
    keytool -export -alias peer2 -file peer2.cer -keystore peer2.jks -storepass passwd2
    
    三、peer1/peer2互相导入对方密钥库,建立信任
    1、peer2的证书导入peer1密钥库
    keytool -import -alias peer2 -file peer2.cer -keystore peer1.jks -storepass passwd1
    2、peer1的证书导入peer2密钥库
    keytool -import -alias peer1 -file peer1.cer -keystore peer2.jks -storepass passwd2
    
    四、检验peer1/peer2是否具有自己的privateKey和对方的cert
    1、检验peer1是否具有自己的privateKey和peer2的cert
    keytool -list -keystore peer1.jks -storepass passwd1
    2、检验peer2是否具有自己的privateKey和peer1的cert
    keytool -list -keystore peer2.jks -storepass passwd2
    
    五、转换JKS格式为P12(便于在postman单向测试证书)
    1、转换peer2.jks->peer2.p12
    keytool -importkeystore -srckeystore peer2.jks -destkeystore peer2.p12 -srcstoretype JKS -deststoretype PKCS12 -srcstorepass passwd2 -deststorepass passwd2 -srckeypass passwd2 -destkeypass passwd2 -srcalias peer2 -destalias peer2 -noprompt
    2、转换peer1.jks->peer1.p12
    keytool -importkeystore -srckeystore peer1.jks -destkeystore peer1.p12 -srcstoretype JKS -deststoretype PKCS12 -srcstorepass passwd1 -deststorepass passwd1 -srckeypass passwd1 -destkeypass passwd1 -srcalias peer1 -destalias peer1 -noprompt
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    4.2.1.2、信任密钥库独立于密钥库
    ## 信任密钥库独立于密钥库
    
    一、peer1生成密钥库以及导出公钥证书
    1、生成peer1的密钥库peer1.jks
    keytool -genkeypair -alias peer1 -keystore peer1.jks -storepass passwd1 -dname CN=peer1,OU=peer1,O=peer1,L=peer1,C=CN
    2、查看密钥库详情
    keytool -list -keystore peer1.jks -storepass passwd1 -v
    3、peer1导出公钥证书
    keytool -export -alias peer1 -file peer1.cer -keystore peer1.jks -storepass passwd1
    
    二、peer2生成密钥库以及导出公钥证书
    1、生成peer2的密钥库peer2.jks
    keytool -genkeypair -alias peer2 -keystore peer2.jks -storepass passwd2 -dname CN=peer2,OU=peer2,O=peer2,L=peer2,C=CN
    2、查看密钥库详情
    keytool -list -keystore peer2.jks -storepass passwd2 -v
    3、peer2导出公钥证书
    keytool -export -alias peer2 -file peer2.cer -keystore peer2.jks -storepass passwd2
    
    三、peer1/peer2互相导入对方信任密钥库
    1、peer2的证书导入peer1信任密钥库
    keytool -import -alias peer1Trust -file peer2.cer -keystore peer1Trust.jks -storepass passwd1
    2、peer1的证书导入peer2信任密钥库
    keytool -import -alias peer2Trust -file peer1.cer -keystore peer2Trust.jks -storepass passwd2
    
    四、检验peer1/peer2是否具有对方的cert
    1、检验peer1的信任密钥库是否有peer2的cert
    keytool -list -keystore peer1Trust.jks -storepass passwd1
    2、检验peer2的信任密钥库是否有peer1的cert
    keytool -list -keystore peer2Trust.jks -storepass passwd2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    4.2.2、CA签发peer1/peer2

    peer1和peer2两个机构各自生成自己秘钥库jks,再生成csr证书请求文件,再通过同一个CA签发生成证书。再将ca和自身证书配置在自己的秘钥库和信任秘钥库中,从而达到互认互信加密通信。信任秘钥库可以合并在秘钥库,也可以是独立于秘钥库的另一个文件中。

    CA的方式更灵活一些,不像自签名的方式,一旦加入了peer3,那么peer1和peer2都要再把peer3的证书导入到自己信任秘钥库中,一般需要重启服务;而CA方式避免了这个问题。

    4.2.2.1、信任密钥库合并在密钥库
    # CA jks
    keytool -genkeypair -alias ca -dname "cn=Local Network - Development" -validity 10000 -keyalg RSA -keysize 2048 -ext bc:c -keystore ca.jks -keypass passwdca -storepass passwdca
    
    # CA cert
    keytool -exportcert -noprompt -rfc -alias ca -file ca.crt -keystore ca.jks -storepass passwdca
    
    keytool -exportcert -noprompt -rfc -alias ca -file ca.pem -keystore ca.jks -storepass passwdca
    
    
    #Peer1
    # generate private keys (for peer1)
    keytool -genkeypair -alias peer1 -dname cn=peer1 -validity 10000 -keyalg RSA -keysize 2048 -keystore peer1.jks -keypass passwd1 -storepass passwd1
    
    # generate a certificate for peer1 signed by ca (ca -> peer1)
    
    # generate csr
    keytool -certreq -noprompt -alias peer1 -sigalg SHA256withRSA -file peer1.csr -keypass passwd1 -keystore peer1.jks -dname "CN=peer1" -storepass passwd1
    
    # sign csr
    keytool -gencert -noprompt -infile peer1.csr -outfile peer1.crt -alias ca -sigalg SHA256withRSA -validity 10000 -ext ku:c=dig,keyEnc -ext "san=dns:localhost,ip:127.0.0.1" -ext eku=sa,ca -keypass passwdca -keystore ca.jks -storepass passwdca -rfc
    
    # import ca.crt into peer1.jks
    keytool -keystore peer1.jks -storepass passwd1 -importcert -trustcacerts -noprompt -alias ca -file ca.crt
    
    # import peer1.crt into peer1.jks (complete crt chain ca->peer1 imported)
    keytool -keystore peer1.jks -storepass passwd1 -importcert -noprompt -alias peer1 -file peer1.crt
    
    
    
    #Peer2
    # generate private keys (for peer2)
    keytool -genkeypair -alias peer2 -dname cn=peer2 -validity 10000 -keyalg RSA -keysize 2048 -keystore peer2.jks -keypass passwd2 -storepass passwd2
    
    # generate a certificate for peer2 signed by ca (ca -> peer2)
    
    # generate csr
    keytool -certreq -noprompt -alias peer2 -sigalg SHA256withRSA -file peer2.csr -keypass passwd2 -keystore peer2.jks -dname "CN=peer2" -storepass passwd2
    
    # sign csr
    keytool -gencert -noprompt -infile peer2.csr -outfile peer2.crt -alias ca -sigalg SHA256withRSA -validity 10000 -ext ku:c=dig,keyEnc -ext "san=dns:localhost,ip:127.0.0.1" -ext eku=sa,ca -keypass passwdca -keystore ca.jks -storepass passwdca -rfc
    
    # import ca.crt into peer2.jks
    keytool -keystore peer2.jks -storepass passwd2 -importcert -trustcacerts -noprompt -alias ca -file ca.crt
    
    # import peer2.crt into peer2.jks (complete crt chain ca->peer2 imported)
    keytool -keystore peer2.jks -storepass passwd2 -importcert -noprompt -alias peer2 -file peer2.crt
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    4.2.2.2、信任密钥库独立于密钥库

    可以单独把ca证书导入到独立的信任库。

    # Trust Store containing ca.crt
    keytool -importcert -noprompt -alias catrust -file ca.crt -keypass passwdtrust -keystore trust.jks -storepass passwdtrust
    
    • 1
    • 2

    4.2.3、ssl服务端配置

    ## ssl start
    server.ssl.pure-key-store=peer1.jks
    server.ssl.pure-trust-store=trust.jks
    # 私钥库
    server.ssl.enabled=true
    server.ssl.key-store-type=JKS
    server.ssl.key-store=classpath:${server.ssl.pure-key-store}
    server.ssl.key-store-password=passwd1
    server.ssl.key-alias=peer1
    # 受信任密钥库
    server.ssl.trust-store=classpath:${server.ssl.pure-trust-store}
    server.ssl.trust-store-password=passwdtrust
    server.ssl.trust-store-provider=SUN
    server.ssl.trust-store-type=JKS
    server.ssl.client-auth=need
    ## ssl end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    4.2.4、ssl客户端配置

    客户端restTemplate底层实现选用httpClient,

    
        org.apache.httpcomponents
        httpclient
        4.5.5
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    构造httpClient的sslContext,

    @Configuration
    @Slf4j
    public class RestTemplateConfig {
    
        @Value("${server.ssl.pure-key-store}")
        public String keyStore;
        @Value("${server.ssl.key-store-password}")
        public String keyStorePassword;
        @Value("${server.ssl.pure-trust-store}")
        public String trustStore;
        @Value("${server.ssl.trust-store-password}")
        public String trustStorePassword;
    
        @Bean
        public RestTemplate restTemplate(ClientHttpRequestFactory httpComponentsClientHttpRequestFactory) {
            RestTemplate restTemplate = new RestTemplate(httpComponentsClientHttpRequestFactory);
            restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
            log.info("loading restTemplate");
            return restTemplate;
        }
    
        @Bean("httpComponentsClientHttpRequestFactory")
        public ClientHttpRequestFactory httpComponentsClientHttpRequestFactory() throws IOException, UnrecoverableKeyException,
                CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
            SSLContext sslContext = SSLContextBuilder
                    .create()
                    // 作为client,load自己密钥库;理论上当server的server.ssl.client-auth=need时,通过它可以获取client的公钥证书传递给server来验证
                    .loadKeyMaterial(new ClassPathResource(keyStore).getURL(),
                            trustStorePassword.toCharArray(), keyStorePassword.toCharArray())
                    // 作为client,load自己的信任库;在请求server之前,client先通过它判断server是否受信
                    .loadTrustMaterial(new ClassPathResource(trustStore).getURL(), trustStorePassword.toCharArray())
                    .build();
            HttpClient client = HttpClients.custom()
                    .setSSLContext(sslContext)
                    .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) // 不需要主机验证
                    .build();
            HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(client);
            return requestFactory;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    4.3、同时开启http

    自定义一个http端口,

    server.http-port=8288
    
    • 1

    配置web容器也开启http端口,

    @Component
    public class TomcatServerCustomer implements WebServerFactoryCustomizer {
        @Value("${server.http-port}")
        public Integer httpPort;
    
        @Override
        public void customize(TomcatServletWebServerFactory factory) {
            Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
            connector.setScheme("http");
            connector.setPort(httpPort);
            factory.addAdditionalTomcatConnectors(connector);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    服务启动后会打印https和http端口信息,

    Tomcat started on port(s): 28288 (https) 8288 (http) with context path ''
    
    • 1

    4.4、测试

    http调用peer,触发内部https调用另一peer,成功返回,

    curl http://127.0.0.1:8688/api/doSomething
    hello, peer2
    curl http://127.0.0.1:8288/api/doSomething
    hello, peer1
    
    • 1
    • 2
    • 3
    • 4

    当然也可以在postman中模拟一个客户端peer2,直接请求https接口https://peer1/interact/reply/hello,只不过需要在Settings-Certificates-Client Certificates导入peer2的证书即可。

    从peer2的jks格式秘钥库中获取p12证书,

    keytool -importkeystore -srckeystore peer2.jks -destkeystore peer2.p12 -srcstoretype JKS -deststoretype PKCS12 -srcstorepass passwd2 -deststorepass passwd2 -srckeypass passwd2 -destkeypass passwd2 -srcalias peer2 -destalias peer2 -noprompt
    
    • 1

    除此之外,还有其他命令,

    查看帮助命令:

    keytool -command_name -help
    keytool -genkeypair -help
    
    • 1
    • 2

    查看密钥库里面的信息:

    keytool -list -keystore peer1.jks -v
    
    • 1

    查看指定证书文件的信息:

    keytool -printcert -file peer1.cer -v
    
    • 1

    cer证书转为pem:

    openssl x509 -in ca.cer -inform DER -out ca.pem -outform PEM
    
    • 1

    五、其他

    5.1、ssl单向认证

    是指peer节点作为服务端,不用去认证客户端,这样每个客户端也不用发送自己的公钥证书给服务端了。对应设置server.ssl.client-auth=none,再注释掉下面不需要的这行,

    SSLContext sslContext = SSLContextBuilder
        .create()
        // 注释这行
        /*.loadKeyMaterial(new ClassPathResource(keyStore).getURL(), trustStorePassword.toCharArray(), keyStorePassword.toCharArray())*/
        ...
    
    • 1
    • 2
    • 3
    • 4
    • 5

    ssl单向认证
    可以看出来,单向认证只是客户端校验服务端证书是否合法。
    ssl双向认证
    双向认证还需要服务端校验客户端证书。
    以上插图来自这里

    5.2、jdk版本兼容问题

    真实场景中使用,需要多测试不同jdk版本下的keytool生成的秘钥证书,在不同版本jvm下的表现。比如jdk11下生成的jks,在jdk8中运行可能报错“Invalid keytool format”。

    5.3、CA方式单向认证在postman的测试

    这种情况下,因为服务端不再需要验证客户端证书,所以postman作为客户端也不需要导入Settings-Certificates-Client Certificates。此时已经可以直接访问https接口了,只是postman里会标记提示“Unable to get local issuer certificate”,如下,
    在这里插入图片描述
    同样,直接在浏览器敲“https://127.0.0.1:28288/interact/reply/hello?name=browser”,也会先提示“NET::ERR_CERT_AUTHORITY_INVALID”,代表服务端的证书可能是不安全的,需要手动继续访问。
    那如果强行开启postman客户端的ssl认证,首先需要如下操作,
    在这里插入图片描述

    此时访问,则报错SSL Error: Unable to get local issuer certificate
    在这里插入图片描述

    所以还需要在Settings-Certificates-CA Certificates导入ca.pem,此时再访问,可能报错SSL Error: Hostname/IP does not match certificate's altnames
    在这里插入图片描述
    这是因为生成节点证书时,通过-ext "san=dns:localhost"指定了只允许通过serverName别名localhost来访问,127.0.0.1不能访问,所以要么请求ip改为localhost,要么在生成证书时,同时指定ip,就可以通过ip访问:-ext "san=dns:localhost,ip:127.0.0.1"

    5.4、多级CA

    上面是ca->peer,只有一级。多级root->ca->peer可以这样,

    5.4.1、root

    ## [root] generate root.jks
    keytool -genkeypair -alias root -dname "cn=root" -validity 10000 -keyalg RSA -keysize 2048 -ext bc:c -keystore root.jks -keypass passwdroot -storepass passwdroot
    
    ## [root] self sign and generate root.pem
    keytool -exportcert -rfc -keystore root.jks -alias root -storepass passwdroot > root.pem
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5.4.2、ca

    ## [ca] generate ca.jks
    keytool -genkeypair -alias ca -dname "cn=ca" -validity 10000 -keyalg RSA -keysize 2048 -ext bc:c -keystore ca.jks -keypass passwdca -storepass passwdca
    ## [ca] generate ca.csr
    keytool -keystore ca.jks -storepass passwdca -certreq -alias ca -file ca.csr
    ## [root] root sign and generate ca.pem (root -> ca)
    keytool -keystore root.jks -storepass passwdroot -gencert -alias root -ext bc=0 -ext san=dns:ca -rfc -infile ca.csr > ca.pem
    
    ## [ca] import root.pem and ca.pem into ca.jks
    keytool -keystore ca.jks -storepass passwdca -importcert -trustcacerts -noprompt -alias root -file root.pem
    keytool -keystore ca.jks -storepass passwdca -importcert -alias ca -file ca.pem
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5.4.3、peer1

    ## [peer1] generate peer1.jks
    keytool -genkeypair -alias peer1 -dname cn=peer1 -validity 10000 -keyalg RSA -keysize 2048 -keystore peer1.jks -keypass passwd1 -storepass passwd1
    ## [peer1] generate peer1.csr
    keytool -keystore peer1.jks -storepass passwd1 -certreq -alias peer1 -file peer1.csr
    ## [ca] ca sign and generate peer1.pem (root -> ca -> peer1)
    keytool -keystore ca.jks -storepass passwdca -gencert -alias ca -ext ku:c=dig,keyEnc -ext "san=dns:localhost,ip:127.0.0.1" -ext eku=sa,ca -rfc -infile peer1.csr > peer1.pem
    
    ## [peer1] import root.pem and ca.pem and peer1.pem into peer1.jks
    keytool -keystore peer1.jks -storepass passwd1 -importcert -trustcacerts -noprompt -alias root -file root.pem
    keytool -keystore peer1.jks -storepass passwd1 -importcert -alias ca -file ca.pem
    keytool -keystore peer1.jks -storepass passwd1 -importcert -alias peer1 -file peer1.pem
    
    ## [peer1] import root.pem and ca.pem and peer1.pem into peer1Trust.jks
    keytool -keystore peer1Trust.jks -storepass passwd1 -importcert -trustcacerts -noprompt -alias root -file root.pem
    keytool -keystore peer1Trust.jks -storepass passwd1 -importcert -alias ca -file ca.pem
    keytool -keystore peer1Trust.jks -storepass passwd1 -importcert -alias peer1 -file peer1.pem
    
    ## [peer1] peer1.jks -> peer1.p12
    keytool -importkeystore -srckeystore peer1.jks -destkeystore peer1.p12 -srcstoretype JKS -deststoretype PKCS12 -srcstorepass passwd1 -deststorepass passwd1 -srckeypass passwd1 -destkeypass passwd1 -srcalias peer1 -destalias peer1 -noprompt
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    5.4.4、peer2

    ## [peer2] generate peer2.jks
    keytool -genkeypair -alias peer2 -dname cn=peer2 -validity 10000 -keyalg RSA -keysize 2048 -keystore peer2.jks -keypass passwd2 -storepass passwd2
    ## [peer2] generate peer2.csr
    keytool -keystore peer2.jks -storepass passwd2 -certreq -alias peer2 -file peer2.csr
    ## [ca] ca sign and generate peer2.pem (root -> ca -> peer2)
    keytool -keystore ca.jks -storepass passwdca -gencert -alias ca -ext ku:c=dig,keyEnc -ext "san=dns:localhost,ip:127.0.0.1" -ext eku=sa,ca -rfc -infile peer2.csr > peer2.pem
    
    ## [peer2] import root.pem and ca.pem and peer2.pem into peer2.jks
    keytool -keystore peer2.jks -storepass passwd2 -importcert -trustcacerts -noprompt -alias root -file root.pem
    keytool -keystore peer2.jks -storepass passwd2 -importcert -alias ca -file ca.pem
    keytool -keystore peer2.jks -storepass passwd2 -importcert -alias peer2 -file peer2.pem
    
    ## [peer2] import root.pem and ca.pem and peer2.pem into peer2Trust.jks
    keytool -keystore peer2Trust.jks -storepass passwd2 -importcert -trustcacerts -noprompt -alias root -file root.pem
    keytool -keystore peer2Trust.jks -storepass passwd2 -importcert -alias ca -file ca.pem
    keytool -keystore peer2Trust.jks -storepass passwd2 -importcert -alias peer2 -file peer2.pem
    
    ## [peer2] peer2.jks -> peer2.p12
    keytool -importkeystore -srckeystore peer2.jks -destkeystore peer2.p12 -srcstoretype JKS -deststoretype PKCS12 -srcstorepass passwd2 -deststorepass passwd2 -srckeypass passwd2 -destkeypass passwd2 -srcalias peer2 -destalias peer2 -noprompt
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    给你的R语言再次提速
    AutoSAR 标准--基础安全特性
    简单好用的 SemVer: 如何命名你的应用版本
    使用 Vert.X Future/Promise 编写异步代码
    简单的Linux服务器端代码
    腾讯业务安全岗 IDP 谈话总结
    游戏品类加速回暖,文娱内容持续火热——2022年IAA行业品类发展洞察系列报告·第三期
    Dunham‘s sports EDI需求分析
    java 开发ide插件
    单臂路由的详细配置步骤
  • 原文地址:https://blog.csdn.net/songzehao/article/details/127780689