草庐IT

OpenSSL 证书

maxmaxma 2025-05-27 原文

目录

证书标准

编码格式

PEM: Privacy Enhanced Mail

DER: Distinguished Encoding Rules

编码转换:

 文件扩展名

自签证书

签发根证书

 生成CA私钥

生成CA申请文件

签发根证书

 导出Java的CA证书

签发服务端证书

签发客户端证书


证书标准

X.509 数字证书标准,定义证书文件的结构和内容。

证数结构图:

编码格式

证书的编码格式:一般包括 PEM 和 DER 两种。

PEM: Privacy Enhanced Mail

DER: Distinguished Encoding Rules

编码转换:

PEM转DER
openssl x509 -in xxx.pem -outform der -out xxx.der

 DER转PEM
openssl x509 -in xxx.der -inform der -outform pem -out xxx.pem

文件扩展名

自签证书

签发根证书

 生成CA私钥

//生成CA私钥:
# openssl genrsa -des3 -out cakey.pem 2048
输入密码:Honeywell@654321

// 查看CA私钥
# openssl rsa -in cakey.pem -text

生成CA申请文件

//根据私钥生成CA申请文件:
# openssl req -new -key cakey.pem -out ca.csr -subj "/C=CN/O=Honeywell/CN=WCS Project(rabbitmq)"
 
//查看CA申请文件
# openssl req -noout -text -in ca.csr

签发根证书

//签发根证书:
# openssl x509 -req -days 3650 -sha1 -extfile /etc/pki/tls/openssl.cnf -extensions v3_ca -signkey cakey.pem -in ca.csr -out ca.cer
 
// 查看证书
# openssl x509 -in ca.cer -text -noout

 

导出p12的CA证书 

openssl pkcs12 -export -clcerts -in ca.cer -inkey cakey.pem -out ca.p12

 导出JKS的CA证书

//导出服务端java程序可用的CA证书:
keytool -import -v -trustcacerts -storepass Honeywell@654321 -alias root -file ca.cer -keystore ca.jks

签发服务端证书

//服务端私钥
openssl genrsa -des3 -out server-key.pem 2048

// 查看私钥
openssl rsa -in server-key.pem -text
 
//服务端申请文件
openssl req -new -key server-key.pem -out server.csr -subj "/C=CN/O=Honeywell/CN=WCS Project(rabbitmq)"
 
//使用根证书签发服务端证书
openssl x509 -req -days 3650 -sha1 -extfile /etc/pki/tls/openssl.cnf -extensions v3_req -CA ca.cer -CAkey cakey.pem -CAserial ca.srl -CAcreateserial -in server.csr -out server.cer
 
//导出服务端p12证书:
openssl pkcs12 -export -clcerts -inkey server-key.pem -in server.cer -out server.p12

//导出服务端java程序可用的JKS证书
keytool -import -v -trustcacerts -storepass Honeywell@654321 -alias server -file ca.cer -keystore server.jks

签发客户端证书

//私钥
openssl genrsa -des3 -out client-key.pem 2048
// 查看私钥
openssl rsa -in client-key.pem -text
 
//客户端申请文件:
openssl req -new -key client-key.pem -out client.csr -subj "/C=CN/O=Honeywell/CN=WCS Project(rabbitmq)"
//查看申请文件
openssl req -text -noout -verify -in client.csr
 
//使用根证书签发客户端证书:
openssl x509 -req -days 3650 -sha256 -extfile /etc/pki/tls/openssl.cnf -extensions v3_req -CA ca.cer -CAkey cakey.pem -CAserial ca.srl -in client.csr -out client.cer
//查看证书
openssl x509 -in client.cer -text -noout
 
导出客户端证书:
openssl pkcs12 -export -clcerts -name wcsclient -inkey client-key.pem -in client.cer -out client.p12
// 查看p12证书
openssl pkcs12 -info -in client.p12
 
//p12导入至keystore
keytool -import -v -trustcacerts -storepass Honeywell@654321 -alias client -file client.cer -keystore client.jks

//查看keystore
keytool -v -list -keystore client.jks

合并证书

合并server.crt和ca.crt

-----BEGIN CERTIFICATE-----

...ca.crt的内容

-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----

...server.crt的内容

-----END CERTIFICATE-----

转换为pkcs12格式

openssl pkcs12 -export -clcerts -in server.crt -inkey server.key -out server.p12

转换为jks格式

keytool -importkeystore -srckeystore server.p12 -destkeystore server.jks -srcstoretype pkcs12 -deststoretype jks

验证证书

keytool -list -v -keystore server.jks

KeyStore

KeyStore是一个存储库,可用于存储一系列密钥、密钥对或证书。

KeyStore的实现方式:JKS(Java Key Store)、PKCS12、JCEKS
JKS:        可以存储密钥对、证书,不能存储密钥。
PKCS12: 可以存储密钥对、证书、密钥。
JCEKS:   可以存储密钥对、证书、密钥。

keytool

keytool是JDK的Keystore管理工具。

JAVA代码

服务端:

package com.xxx.yyy.common.socket.utils;

import cn.hutool.json.JSONUtil;
import com.xxx.yyy.common.socket.config.SocketServerTLSConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.*;
import java.security.cert.CertificateException;

/**
 * keystore用于存放自己的密钥和公钥,
 * truststore用于存放所有需要信任方的公钥
 * SSLContext:SSLContext类可以创建SSLSocket实例,在这个类中配置自己的证书和自己所信任的证书。
 * KeyManagerFactory:加载自己的证书文件,并且生成KeyManager[]数据,配置到SSLContext。
 * TrustManagerFactory:加载自己信任的客户端证书,双向认证的时候配置自己信任哪些服务端证书。
 * KeyStore:加载证书的类。
 * TrustManager[]:信任的证书信任的Manager数组。
 * Manager[]:自己的证书的Manager数组。
 *
 * @author H443745
 */
public class SslSocketServerUtil {
    private static final Logger logger = LoggerFactory.getLogger(SslSocketServerUtil.class);

    private static final String PROTOCOL = "TLSv1.2";
    public static final String KEYSTORE_TYPE = "JKS";
    //KeyStore的类型
    private static final String PROVIDER = "SunX509";

    private SslSocketServerUtil() {
    }

    /**
     * 创建服务端socket
     *
     * @param tlsConfig
     * @return
     * @throws Exception
     */
    public static SSLServerSocket createSslServerSocket(SocketServerTLSConfig tlsConfig) throws IOException {
        SSLContext sslContext = createContext(tlsConfig);
        if (sslContext == null) {
            return null;
        }
        SSLServerSocketFactory serverSocketFactory = sslContext.getServerSocketFactory();
        // 绑定服务端口
        SSLServerSocket serverSocket = (SSLServerSocket) serverSocketFactory.createServerSocket(tlsConfig.getPort());
        // 设置加密算法套件
        String[] supported = serverSocketFactory.getSupportedCipherSuites();
        serverSocket.setEnabledCipherSuites(supported);
        logger.info("服务端可支持的加密算法套件有:{}", supported);

        //设置这个SSLServerSocket需要授权的客户端访问, 是否开启双向验证
        serverSocket.setNeedClientAuth(tlsConfig.isServerModeEnabled());

        return serverSocket;
    }

    /**
     * 创建上下文环境,主要用于保存安全通信的基本信息,如协议版本、证书管理方式
     *
     * @param tlsConfig
     * @return
     * @throws Exception
     */
    public static SSLContext createContext(SocketServerTLSConfig tlsConfig) {
        logger.info("开始服务端创建sslContext上下文......");

        //服务端证书库
        KeyStore serverKeystore = null;
        FileInputStream fileInputStream = null;

        KeyStore trustKeystore = null;
        FileInputStream trustFileInputStream = null;

        try {
            serverKeystore = KeyStore.getInstance(KEYSTORE_TYPE);
            fileInputStream = new FileInputStream(tlsConfig.getServerKeystorePath());
            serverKeystore.load(fileInputStream, tlsConfig.getServerKeystorePassword().toCharArray());

            /// KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            //生成秘钥的manager
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(PROVIDER);
            // 管理本端 的密钥(发送本端的证书用于对方认证我的身份)
            logger.info(KeyManagerFactory.getDefaultAlgorithm());

            //秘钥初始化
            kmf.init(serverKeystore, tlsConfig.getServerKeystorePassword().toCharArray());

            SSLContext sslContext = SSLContext.getInstance(PROTOCOL);
            logger.info("缺省安全套接字使用的协议:{} ", sslContext.getProtocol());

            //是否双向验证
            boolean serverModeEnabled = tlsConfig.isServerModeEnabled();
            if(serverModeEnabled){
                // 管理对端证书的信任证书,用于认证对方的身份
                trustKeystore = KeyStore.getInstance(KEYSTORE_TYPE);
                trustFileInputStream = new FileInputStream(tlsConfig.getServerTrustKeystorePath());
                trustKeystore.load(trustFileInputStream, tlsConfig.getServerTrustKeystorePassword().toCharArray());

                // 信任库
                TrustManagerFactory tmf = TrustManagerFactory.getInstance(PROVIDER);
                // 信任证书和服务端私钥 放在同一个库里
                tmf.init(trustKeystore);
                // 上下文初始化,双向,两端证书的加载
                sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
            } else {
                // 上下文初始化,单向
                sslContext.init(kmf.getKeyManagers(), null, null);
            }

            logger.info("上下文信息:{}", sslContext.getProtocol());
            logger.info("支持的协议:{}", JSONUtil.toJsonStr(sslContext.getSupportedSSLParameters().getProtocols()));


            return sslContext;
        } catch (KeyStoreException | KeyManagementException | NoSuchAlgorithmException | UnrecoverableKeyException |
                 CertificateException | IOException e) {
            logger.info("Exception");
            e.printStackTrace();
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (trustFileInputStream != null) {
                try {
                    trustFileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return null;
    }

}

客户端:

package com.xxx.yyy.common.socket.utils;

import com.xxx.yyy.common.socket.config.SocketClientTLSConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;

/**
 * @author H443745
 */
public class SslSocketClientUtil {
    private static final Logger logger = LoggerFactory.getLogger(SslSocketClientUtil.class);

    public static final String PROTOCOL = "TLSv1.2";
    public static final String KEYSTORE_TYPE = "JKS";

    private static final String PROVIDER = "SunX509";

    private SslSocketClientUtil() {
    }

    public static SSLContext createSslContext(SocketClientTLSConfig tlsClientConfig) {

        //客户端证书库
        FileInputStream fileInputStream = null;
        KeyStore clientKeystore = null;

        //信任证书库
        FileInputStream trustFileInputStream = null;
        KeyStore trustKeystore = null;

        try {
            //信任证书库
            trustKeystore = KeyStore.getInstance(KEYSTORE_TYPE);
            trustFileInputStream = new FileInputStream(tlsClientConfig.getClientTrustKeystorePath());
            trustKeystore.load(trustFileInputStream, tlsClientConfig.getClientTrustKeystorePassword().toCharArray());

            //信任库
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(PROVIDER);
            tmf.init(trustKeystore);

            //初始化SSL上下文
            SSLContext ctx = SSLContext.getInstance(PROTOCOL);
            logger.info("缺省安全套接字使用的协议:{} ", ctx.getProtocol());

            //是否双向验证
            boolean clientModeEnabled = tlsClientConfig.isClientModeEnabled();
            if(clientModeEnabled){
                //客户端证书库
                clientKeystore = KeyStore.getInstance(KEYSTORE_TYPE);
                fileInputStream = new FileInputStream(tlsClientConfig.getClientKeystorePath());
                clientKeystore.load(fileInputStream, tlsClientConfig.getClientKeystorePassword().toCharArray());

                // 密钥库
                /// KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
                KeyManagerFactory kmf = KeyManagerFactory.getInstance(PROVIDER);
                kmf.init(clientKeystore, tlsClientConfig.getClientKeystorePassword().toCharArray());

                ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
            } else{
                ctx.init(null, tmf.getTrustManagers(), null);
            }

            return ctx;
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("failed to load KeyStore.", e);
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (trustFileInputStream != null) {
                try {
                    trustFileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return null;
    }

    @SuppressWarnings("unchecked")
    public static SSLSocketFactory createSocketFactory(SocketClientTLSConfig tlsClientConfig) {
        SSLContext sslContext = createSslContext(tlsClientConfig);
        if (sslContext == null) {
            logger.error("failed to create SSLSocketFactory.");
            return null;
        }
        logger.info("Protocol:{}", sslContext.getProtocol());
        return sslContext.getSocketFactory();
    }

    public static SSLSocket createSocket(SocketClientTLSConfig tlsClientConfig, String host, int port, HostnameVerifier hostnameVerifier) throws SSLException {
        SSLSocketFactory ssf = createSocketFactory(tlsClientConfig);
        if (ssf == null) {
            logger.error("SSLSocketFactory should not be null.");
            return null;
        }

        SSLSocket socket = null;
        try {
            socket = (SSLSocket) ssf.createSocket(host, port);
            // TODO-WANG later need TLSv1.3
            // socket.setEnabledProtocols(new String[]{"TLSv1.3"});
            socket.setSoTimeout(10000);
        } catch (IOException e) {
            logger.error("failed to createSocket, host={}, port={}", host, port, e);
        }

        if (socket == null) {
            logger.error("SSLSocketFactory not created.");
            return null;
        }
        if (hostnameVerifier != null && !hostnameVerifier.verify(socket.getInetAddress().getHostName(), socket.getSession())) {
            throw new SSLException("\"" + socket.getInetAddress().getHostName() + "\" identity was not confirmed");
        }

        return socket;
    }
}

有关OpenSSL 证书的更多相关文章

  1. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  2. c# - 在 C# 中重现 Ruby OpenSSL private_encrypt 输出 - 2

    我有一个简单的Ruby脚本,我用它在某些HTTPheader上执行private_encrypt以签署要发送到ruby​​RESTAPI的Web请求,该API会根据Base64编码字符串测试Base64编码字符串生成而不是解码Base64和解密数据然后测试原始字符串。我使用的脚本是require"openssl"require"base64"path_to_cert=ARGV[0].dupplain_text=Base64.decode64(ARGV[1].dup)private_key=OpenSSL::PKey::RSA.new(File.read(path_to_cert))pu

  3. ruby - 使用 OpenSSL ruby​​ 从一个 .p12 文件中提取多个 key - 2

    我想知道如何从Apple.p12文件中提取key。根据我有限的理解,.p12文件是X504证书和私钥的组合。我看到我遇到的每个.p12文件都有一个X504证书和至少一个key,在某些情况下有两个key。这是因为每个.p12都有一个Apple开发人员key,有些还有一个额外的key(可能是Appleroot授权key)。我只考虑那些具有两个key的.p12文件是有效的。我的目标是区分具有一个key的.p12文件和具有两个key的.p12文件。到目前为止,我已经使用OpenSSL来检查X504文件和任何.p12的key。例如,我有这段代码可以检查目录中的所有.p12文件:Dir.glob(

  4. ruby - 如何获得带有 SSL 客户端证书的 HTTPS 请求以与 Ruby EventMachine 一起使用? - 2

    我正在尝试使用RubyEventMachine访问使用SSL证书身份验证的HTTPSWeb服务,但我没有让它工作。我编写了以下简单代码块来对其进行端到端测试:require'rubygems'require'em-http'EventMachine.rundourl='https://foobar.com/'ssl_opts={:private_key_file=>'/tmp/private.key',:cert_chain_file=>'/tmp/ca.pem',:verify_peer=>false}http=EventMachine::HttpRequest.new(url).g

  5. 更新证书后,Ruby Net::HTTP 响应 OpenSSL::SSL::SSLError "certificate verify failed" - 2

    我们最近更新了我们网站的SSL证书,在MacOSElCapitan10.11.3上出现以下情况:require'net/http'Net::HTTP.getURI('https://www.google.com')#=>"..."#ThesitewhosecertificategotrenewedNet::HTTP.getURI('https://www.example.com')#=>OpenSSL::SSL::SSLError:SSL_connectreturned=1errno=0state=error:certificateverifyfailed我在Google和StackO

  6. ruby - 为什么 openssl 在 windows 上产生错误但在 centos 上不产生错误:PKCS12_parse: mac verify failure (OpenSSL::PKCS12::PKCS12Error) - 2

    require'openssl'ifARGV.length==2pkcs12=OpenSSL::PKCS12.new(File.read(ARGV[0]),ARGV[1])ppkcs12.certificateelseputs"Usage:load_cert.rb"end运行它会在Windows上产生错误,但在Linux上不会。错误:OpenSSL::PKCS12::PKCS12Error:PKCS12_parse:macverifyfailurefrom(irb):21:ininitializefrom(irb):21:innewfrom(irb):21fromC:/Ruby192/

  7. Ruby OpenSSL 非对称加密——使用两个 key 对 - 2

    我想使用两个key对在两个通信系统之间实现具有不可否认性的安全消息传递。我使用以下方法生成并存储了两组key对:sys1_key=OpenSSL::PKey::RSA.generate(2048)sys2_key=OpenSSL::PKey::RSA.generate(2048)这两个key对都将其单独的公钥和私钥保存到文件中:sys1.pub.pemsys1.priv.pemsys2.pub.pemsys2.priv.pem系统1有自己的公钥和私钥以及系统2的公钥。系统2有自己的公钥和私钥以及系统1的公钥。在系统1上,我想获取消息“Helloworld”并使用系统1的私钥和系统2的公

  8. ruby - OpenSSL 验证来自自己 CA 的证书 - 2

    大家好,感谢您花时间阅读本文。我需要验证由我自己的CA颁发的证书,我有一个证书。我怎样才能做相当于openssl的openssl验证-CAfile在Ruby代码中?OpenSSL的RDoc在这方面不是很有帮助。我试过:require'openssl'ca=OpenSSL::X509::Certificate.new(File.read('ca-cert.pem'))lic=OpenSSL::X509::Certificate.new(File.read('cert.pem'))putslic.verify(ca)但我得到:test.rb:7:in`verify':wrongargume

  9. ruby-on-rails - 使用 OpenSSL 读取证书文件时出现 Ruby 错误 - 2

    我想做一个简单的OpenSSL::X509::Certificate.new(File.read("testuser.p12"))来自带有ruby1.8.7(或1.9.2)的irb,两者的结果相同。我得到的错误是OpenSSL::X509::CertificateError:nestedasn1error这是ruby问题,还是表明证书本身格式不正确?我发现一些类似的报告围绕着证明此类错误的亚马逊证书展开,结果证明是证书本身。它虽然在浏览器中工作。有关如何解决此问题的建议? 最佳答案 根据后缀,“testuser.p12”似乎是一个P

  10. Ruby Gem Twitter - 证书验证失败(Twitter::Error::ClientError) - 2

    我正在尝试使用ruby​​gem'twitter',但由于未知原因我无法使用它。这是.rb代码:require'twitter'puts"Greetings,World!"puts"Checkpoint1"Twitter.configuredo|config|config.consumer_key="xxxxxxx"#removedforpostingconfig.consumer_secret="xxxxxxx"#removedforpostingconfig.oauth_token="xxxxxxx"#removedforpostingconfig.oauth_token_secr

随机推荐