依赖 Java SSL相关功能需要引入bouncycastle相关依赖,它与java.security有很好的集成
1 2 3 4 5 <dependency > <groupId > org.bouncycastle</groupId > <artifactId > bcpkix-jdk15on</artifactId > <version > 1.70</version > </dependency >
代码 下面是根据bouncycastle实现的一套代码
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 47 48 49 50 51 52 53 54 55 56 public static void main (String[] args) throws Exception { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA" ); keyPairGenerator.initialize(2048 ); KeyPair keyPair = keyPairGenerator.generateKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); PublicKey publicKey = keyPair.getPublic(); StringWriter stringWriter = new StringWriter (); JcaPEMWriter pemWriter = new JcaPEMWriter (stringWriter); pemWriter.writeObject(privateKey); pemWriter.close(); FileOutputStream fos = new FileOutputStream ("C:\\Users\\pengtong\\Desktop\\cert\\certificate.key" ); fos.write(stringWriter.toString().getBytes()); fos.close(); JcaX509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder ( new X500Principal ("CN=localhost" ), BigInteger.valueOf(System.currentTimeMillis()), new Date (), new Date (System.currentTimeMillis() + 365 * 24 * 60 * 60 * 1000 ), new X500Principal ("CN=localhost" ), publicKey ); DERSequence subjectAlternativeNames = new DERSequence (new ASN1Encodable [] { new GeneralName (GeneralName.dNSName, "localhost" ), new GeneralName (GeneralName.iPAddress, "127.0.0.1" ), }); certificateBuilder.addExtension(Extension.subjectAlternativeName, false , subjectAlternativeNames); X509CertificateHolder certificateHolder = certificateBuilder.build(new JcaContentSignerBuilder ("SHA256WITHRSA" ).build(privateKey)); StringWriter stringWriter1 = new StringWriter (); JcaPEMWriter pemWriter1 = new JcaPEMWriter (stringWriter1); pemWriter1.writeObject(certificateHolder); pemWriter1.close(); FileOutputStream fos1 = new FileOutputStream ("C:\\Users\\pengtong\\Desktop\\cert\\certificate.crt" ); fos1.write(stringWriter1.toString().getBytes()); fos1.close(); System.out.println("Certificate generated successfully." ); }
对于证书的生成,还可以参考netty的io.netty.handler.ssl.util.SelfSignedCertificate类的构造方法,其中包括bouncycastle和sun.security两套生成证书的方法
代码解释 具体参数意思可以参考浏览器中的证书信息,https网站都会有一个证书,无论是有效的还是无效的
JcaX509v3CertificateBuilder:代码中的JcaX509v3CertificateBuilder使用了六个构造函数的方法,分别是
Issuer:颁发者,对应上图的颁发者
SerialNumber:序列号,对应上图的序列号
StartDate:有效期-不早于,对应上图的有效期-不早于
EndDate:有效期-不晚于,对应上图的有效期-不晚于
Subject:主题背景,其包含被授权网站,对应上图的主题背景
SubjectPublicKeyInfo:证书持有者的公共密钥信息,对应上图的证书持有者公共密钥信息
certificateBuilder.addExtension:这是JcaX509v3CertificateBuilder的addExtension方法,它是由参数的,只是在代码中没写,因为注释了,所以没有影响而已,它的作用是配置扩展程序,就是上图的扩展程序
new JcaContentSignerBuilder(“SHA256WITHRSA”).build(privateKey):这是JcaContentSignerBuilder的构建方法,返回的是一个ContentSigner对象,其中两部分的作用分别是:
new JcaContentSignerBuilder(“SHA256WITHRSA”):ContentSigner的算法是SHA256WITHRSA,为带“有 RSA 加密的 SHA-256”,即上图中的sha-256指纹
build(privateKey):设置密钥,这里的密钥和上面的SubjectPublicKeyInfo中的公钥必须是密钥对,在上图不会有展示
JcaPEMWriter:因为生成的证书都是二进制格式的,即der格式,nginx无法使用,需要JcaPEMWriter转为文本格式,即pem格式
让浏览器信任自签名
如果直接使用生成等待自签名证书,浏览器会显示以上警告,显示操作系统不信任该证书,一般CA认证中心发布的证书,都是直接得到操作系统信任的,因为我们是自签名证书,所以不会直接得到信任,这时候我们将证书加入到操作系统信任
双击生成的crt文件(pem文件无法直接打开,所以生成crt),点击安装证书,将证书放入到“受信任的根证书颁发机构”,这时候重启浏览器,重新访问
这时候浏览器有报警显示该证书没有指定主题备用名称,这个可能是每个浏览器有限制,我们为证书添加主题备用名称并重新生成
1 2 3 4 5 DERSequence subjectAlternativeNames = new DERSequence (new ASN1Encodable [] { new GeneralName (GeneralName.dNSName, "localhost" ), new GeneralName (GeneralName.iPAddress, "127.0.0.1" ), }); certificateBuilder.addExtension(Extension.subjectAlternativeName, false , subjectAlternativeNames);
生成后,按照上述步骤,将证书添加操作系统信任中,重新打开浏览器访问
看到上面灰色的锁,证明证书已经通过浏览器,没有报警
der、cer、pem .der扩展名 DER是一种编码方法,本身可以表示任何类型的数据,但通常用来编码证书。证书的结构使用ASN.1 (Abstract Syntax Notation One 一种数据描述语言)描述。 BER和DER都是二进制编码方法。
.pem扩展名 PEM是一种将二进制数据编码为字符串的方法。它包含header和footer,用来指定数据的开始和结束,header和footer中间是base64数据。如果数据是证书,那么会简单的编码DER证书。crt格式是pem的证书格式,pem扩展名文件无法打开,需要将扩展名替换成crt才会被定义为证书文件 。都是PEM代表Privacy Enhanced Mail;PEM格式如下
1 2 3 -----BEGIN <whatever>----- data -----END <whatever>----
whatever可以是private keys, public keys, X509 certificates,比如
1 2 3 4 -----BEGIN CERTIFICATE----- ... base 64 encoding of the DER encoded certificate with line endings and padding with equals signs ... -----END CERTIFICATE-----
代码中生成的pem文件即为上述格式
1 2 3 -----BEGIN PRIVATE KEY----- base 64 encoding of the private key -----END PRIVATE KEY-----
代码中生成的key文件即为上述格式
PEM文件也可以包含一个完整的证书链,证书链以叶子/结尾证书服务开始,紧跟着签名它的证书,通常直到根证书(一般不包含根证书)。所以如果缺失证书,你也会首先check一下第一个证书。。由此可知PEM既可以包含公钥、也可以包含私钥。
.cer扩展名 .cer指的是证书certificate,通常是DER编码格式的,但是windows也可以接受PEM格式。