标签:style class code java http tar
双向认证一般使用在B2B系统或企业内部系统中,目的就是阻止无关人员访问系统,哪怕就一个登录页面也不行。只有系统管理员给你发放了证书,你才能访问到该系统。
1. 你系统中要有JDK
2. 你要有一个Servlet容器,这里使用tomcat7
3. 下载BouncyCastle安全工具包,如果你使用Maven管理依赖,请加入下面的依赖项
1
2
3
4
5
6
7
8
9
10 |
< dependency > < groupId >org.bouncycastle</ groupId > < artifactId >bcprov-jdk15on</ artifactId > < version >1.50</ version > </ dependency > < dependency > < groupId >org.bouncycastle</ groupId > < artifactId >bcpkix-jdk15on</ artifactId > < version >1.50</ version > </ dependency > |
1. 生成一个自签名的CA证书
使用JDK自带的keytool工具生成CA证书
1
2
3 |
keytool -genkeypair - alias
"Daojoo.com Co.Ltd. Root CA" -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -dname "CN=Daojoo.com Co.Ltd. Root CA,OU=CA Center,O=Daojoo.com Co.Ltd.,C=CN"
-keypass www.daojoo.com -validity 36135 -storetype PKCS12 -keystore "E:\Daojoo.com Co.Ltd. Root CA.pfx"
-storepass www.daojoo.com keytool -exportcert - alias
"Daojoo.com Co.Ltd. Root CA" - file
"E:\Daojoo.com Co.Ltd. Root CA.cer"
-storetype PKCS12 -keystore "E:\Daojoo.com Co.Ltd. Root CA.pfx"
-storepass www.daojoo.com -rfc |
第一条命令生成pfx格式的keystore,包含私钥;第二条命令导出CA证书。
编程生成CA证书
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 |
// CA 别名 String caAlias = "Daojoo.com Co.Ltd. Root CA" ; // CA 证书保护密码 char [] password = "www.daojoo.com" .toCharArray(); X500NameBuilder x500NameBuilder = new
X500NameBuilder(BCStrictStyle.INSTANCE); x500NameBuilder.addRDN(BCStyle.C, "CN" ); x500NameBuilder.addRDN(BCStyle.O, "Daojoo.com Co.Ltd." ); x500NameBuilder.addRDN(BCStyle.OU, "CA Center" ); x500NameBuilder.addRDN(BCStyle.CN, "Daojoo.com Co.Ltd. Root CA" ); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( "RSA" ); keyPairGenerator.initialize( 2048 ); KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 证书签发者 X500Name issuer = x500NameBuilder.build(); // 证书序列号 BigInteger serial = BigInteger.valueOf( 0 ); // 证书开始有效期 Date notBefore = new
Date(); // 证书结束有效期,99年 Date notAfter = new
Date(notBefore.getTime() + 1000L * 3600
* 24
* 365
* 99 ); // 证书使用者 X500Name subject = issuer; // CA证书公钥 PublicKey caPublicKey = keyPair.getPublic(); // CA证书私钥 PrivateKey caPrivateKey = keyPair.getPrivate(); JcaX509v3CertificateBuilder x509v3CertificateBuilder = new
JcaX509v3CertificateBuilder( issuer, serial, notBefore, notAfter, subject, caPublicKey ); JcaX509ExtensionUtils x509ExtensionUtils = new
JcaX509ExtensionUtils(); // 证书扩展 基本约束 关键 可签发证书 x509v3CertificateBuilder.addExtension(Extension.basicConstraints, true , new
BasicConstraints( true )); // 证书扩展 签发者密钥标识 非关键 x509v3CertificateBuilder.addExtension(Extension.authorityKeyIdentifier, false , x509ExtensionUtils.createAuthorityKeyIdentifier(caPublicKey)); // 证书扩展 使用者密钥标识 非关键 x509v3CertificateBuilder.addExtension(Extension.subjectKeyIdentifier, false , x509ExtensionUtils.createSubjectKeyIdentifier(caPublicKey)); // 证书扩展 密钥用法 关键 (用于吊销列表签名、证书签名) x509v3CertificateBuilder.addExtension(Extension.keyUsage, true , new
KeyUsage( KeyUsage.cRLSign | KeyUsage.keyCertSign)); ContentSigner signer = new
JcaContentSignerBuilder( "SHA1withRSA" ).build(caPrivateKey); X509CertificateHolder x509CertificateHolder = x509v3CertificateBuilder.build(signer); JcaX509CertificateConverter certificateConverter = new
JcaX509CertificateConverter(); Certificate caCertificate = certificateConverter.getCertificate(x509CertificateHolder); // 导出根证书 OutputStream caOs = new
FileOutputStream(caAlias + ".cer" ); caOs.write(caCertificate.getEncoded()); caOs.close(); // 导出为PKCS12 // 将证书、私钥及信任链保存到PKCS12格式的文件中 OutputStream os = new
FileOutputStream(caAlias + ".pfx" ); KeyStore eeKeyStore = KeyStore.getInstance( "PKCS12" , "BC" ); eeKeyStore.load( null , null ); eeKeyStore.setCertificateEntry(caAlias, caCertificate); eeKeyStore.setKeyEntry(caAlias, caPrivateKey, password, new
Certificate[]{caCertificate}); eeKeyStore.store(os, password); |
2. 生成服务器证书
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 |
// CA 别名 String caAlias = "Daojoo.com Co.Ltd. Root CA" ; // CA 证书保护密码 char [] caPassword = "www.daojoo.com" .toCharArray(); // 使用者别名 String eeAlias = "*.daojoo.com" ; // 使用者密码 char [] eePassword = "daojoo.com" .toCharArray(); KeyStore caKeyStore = KeyStore.getInstance( "PKCS12" , "BC" ); caKeyStore.load( new
FileInputStream(caAlias + ".pfx" ), caPassword); Enumeration<String> aliases = caKeyStore.aliases(); // CA 证书 X509Certificate caCertificate = null ; // CA 私钥 PrivateKey caPrivateKey = null ; while
(aliases.hasMoreElements()) { String alias = aliases.nextElement(); if
(caKeyStore.isKeyEntry(alias)) { caCertificate = (X509Certificate) caKeyStore.getCertificate(alias); caPrivateKey = (PrivateKey) caKeyStore.getKey(alias, caPassword); } } X500NameBuilder x500NameBuilder = new
X500NameBuilder(); x500NameBuilder.addRDN(BCStyle.C, "CN" ); x500NameBuilder.addRDN(BCStyle.O, "Daojoo.com Co.Ltd." ); x500NameBuilder.addRDN(BCStyle.OU, "Web Servers" ); x500NameBuilder.addRDN(BCStyle.CN, eeAlias); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( "RSA" ); keyPairGenerator.initialize( 2048 ); KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 证书使用者 X500Name subject = x500NameBuilder.build(); // 证书签发者 X500Name issuer = X500Name.getInstance(caCertificate.getIssuerX500Principal().getEncoded()); // 证书开始有效期 Date notBefore = new
Date(); // 证书结束有效期 以有效期为1年计算 Date notAfter = new
Date(notBefore.getTime() + 1000L * 3600
* 24
* 365 ); // 证书序列号 BigInteger serial = BigInteger.valueOf( 1 ); // 证书公钥 PublicKey eePublicKey = keyPair.getPublic(); // 证书私钥 PrivateKey eePrivateKey = keyPair.getPrivate(); X509v3CertificateBuilder x509v3CertificateBuilder = new
JcaX509v3CertificateBuilder( caCertificate, serial, notBefore, notAfter, subject, eePublicKey ); JcaX509ExtensionUtils x509ExtensionUtils = new
JcaX509ExtensionUtils(); // 证书扩展 基本约束 关键 不签发证书 x509v3CertificateBuilder.addExtension(Extension.basicConstraints, true , new
BasicConstraints( false )); // 证书扩展 签发者密钥标识 非关键 x509v3CertificateBuilder.addExtension(Extension.authorityKeyIdentifier, false , x509ExtensionUtils.createAuthorityKeyIdentifier(caCertificate)); // 证书扩展 使用者密钥标识 非关键 x509v3CertificateBuilder.addExtension(Extension.subjectKeyIdentifier, false , x509ExtensionUtils.createSubjectKeyIdentifier(keyPair.getPublic())); // 证书扩展 密钥用法 关键 (数字签名、数据加密、密钥加密) x509v3CertificateBuilder.addExtension(Extension.keyUsage, true , new
KeyUsage( KeyUsage.digitalSignature | KeyUsage.dataEncipherment | KeyUsage.keyEncipherment)); // 证书扩展 增强密钥用法 关键 (客户端认证、服务器认证) x509v3CertificateBuilder.addExtension(Extension.extendedKeyUsage, true , new
ExtendedKeyUsage( new
KeyPurposeId[]{KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth})); // 为证书签名 ContentSigner signer = new
JcaContentSignerBuilder( "SHA1withRSA" ).build(caPrivateKey); X509CertificateHolder x509CertificateHolder = x509v3CertificateBuilder.build(signer); JcaX509CertificateConverter x509CertificateConverter = new
JcaX509CertificateConverter(); // 得到使用者证书 Certificate endEntityCertificate = x509CertificateConverter.getCertificate(x509CertificateHolder); // 导出根证书 KeyStore endEntityKeyStore = KeyStore.getInstance( "JKS" ); endEntityKeyStore.load( null , null ); endEntityKeyStore.setCertificateEntry(caAlias, caCertificate); // 证书链,最终证书在前、CA证书在后 Certificate[] chain = new
Certificate[]{endEntityCertificate, caCertificate}; endEntityKeyStore.setKeyEntry(eeAlias, keyPair.getPrivate(), eePassword, chain); endEntityKeyStore.store( new
FileOutputStream(eeAlias.substring( 2 , eeAlias.length()) + ".jks" ), eePassword); |
3. 配置tomcat
将生成的服务器证书daojoo.com.jks拷贝到tomcat的conf目录下
在server.xml中添加一个Connector
1
2
3
4
5
6
7
8
9
10
11
12
13 |
< Connector
port = "443"
protocol = "org.apache.coyote.http11.Http11Protocol" connectionTimeout = "20000" SSLEnabled = "true" scheme = "https" secure = "true" clientAuth = "true" sslProtocol = "TLS" keystoreFile = "conf/daojoo.com.jks" keystorePass = "daojoo.com" truststoreFile = "conf/daojoo.com.jks" truststorePass = "daojoo.com" URIEncoding = "UTF-8"
/> |
4. 生成客户端证书
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93 |
// CA 别名 String caAlias = "Daojoo.com Co.Ltd. Root CA" ; // CA 证书保护密码 char [] caPassword = "www.daojoo.com" .toCharArray(); // 使用者别名 String eeAlias = "daojoo" ; // 使用者密码 char [] eePassword = "daojoo" .toCharArray(); KeyStore caKeyStore = KeyStore.getInstance( "PKCS12" , "BC" ); caKeyStore.load( new
FileInputStream(caAlias + ".pfx" ), caPassword); Enumeration<String> aliases = caKeyStore.aliases(); // CA 证书 X509Certificate caCertificate = null ; // CA 私钥 PrivateKey caPrivateKey = null ; while
(aliases.hasMoreElements()) { String alias = aliases.nextElement(); if
(caKeyStore.isKeyEntry(alias)) { caCertificate = (X509Certificate) caKeyStore.getCertificate(alias); caPrivateKey = (PrivateKey) caKeyStore.getKey(alias, caPassword); } } X500NameBuilder x500NameBuilder = new
X500NameBuilder(); x500NameBuilder.addRDN(BCStyle.C, "CN" ); x500NameBuilder.addRDN(BCStyle.ST, "Beijing" ); x500NameBuilder.addRDN(BCStyle.L, "Beijing" ); x500NameBuilder.addRDN(BCStyle.E, "daojoo@daojoo.com" ); x500NameBuilder.addRDN(BCStyle.O, "Daojoo.com Co.Ltd." ); x500NameBuilder.addRDN(BCStyle.OU, "Tech" ); x500NameBuilder.addRDN(BCStyle.CN, "daojoo" ); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( "RSA" ); keyPairGenerator.initialize( 2048 ); KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 证书使用者 X500Name subject = x500NameBuilder.build(); // 证书签发者 X500Name issuer = X500Name.getInstance(caCertificate.getIssuerX500Principal().getEncoded()); // 证书开始有效期 Date notBefore = new
Date(); // 证书结束有效期 以有效期为1年计算 Date notAfter = new
Date(notBefore.getTime() + 1000L * 3600
* 24
* 365 ); // 证书序列号 BigInteger serial = BigInteger.valueOf( 2 ); // 证书公钥 PublicKey eePublicKey = keyPair.getPublic(); // 证书私钥 PrivateKey eePrivateKey = keyPair.getPrivate(); X509v3CertificateBuilder x509v3CertificateBuilder = new
JcaX509v3CertificateBuilder( caCertificate, serial, notBefore, notAfter, subject, eePublicKey ); JcaX509ExtensionUtils x509ExtensionUtils = new
JcaX509ExtensionUtils(); // 证书扩展 基本约束 关键 不签发证书 x509v3CertificateBuilder.addExtension(Extension.basicConstraints, true , new
BasicConstraints( false )); // 证书扩展 签发者密钥标识 非关键 x509v3CertificateBuilder.addExtension(Extension.authorityKeyIdentifier, false , x509ExtensionUtils.createAuthorityKeyIdentifier(caCertificate)); // 证书扩展 使用者密钥标识 非关键 x509v3CertificateBuilder.addExtension(Extension.subjectKeyIdentifier, false , x509ExtensionUtils.createSubjectKeyIdentifier(keyPair.getPublic())); // 证书扩展 密钥用法 关键 (数字签名、数据加密、密钥加密) x509v3CertificateBuilder.addExtension(Extension.keyUsage, true , new
KeyUsage( KeyUsage.digitalSignature | KeyUsage.dataEncipherment | KeyUsage.keyEncipherment)); // 证书扩展 增强密钥用法 关键 (客户端认证、服务器认证) x509v3CertificateBuilder.addExtension(Extension.extendedKeyUsage, true , new
ExtendedKeyUsage( new
KeyPurposeId[]{KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth})); // 为证书签名 ContentSigner signer = new
JcaContentSignerBuilder( "SHA1withRSA" ).build(caPrivateKey); X509CertificateHolder x509CertificateHolder = x509v3CertificateBuilder.build(signer); JcaX509CertificateConverter x509CertificateConverter = new
JcaX509CertificateConverter(); // 得到使用者证书 Certificate endEntityCertificate = x509CertificateConverter.getCertificate(x509CertificateHolder); // 导出根证书 KeyStore endEntityKeyStore = KeyStore.getInstance( "PKCS12" , "BC" ); endEntityKeyStore.load( null , null ); endEntityKeyStore.setCertificateEntry(caAlias, caCertificate); // 证书链,最终证书在前、CA证书在后 Certificate[] chain = new
Certificate[]{endEntityCertificate, caCertificate}; endEntityKeyStore.setKeyEntry(eeAlias, keyPair.getPrivate(), eePassword, chain); endEntityKeyStore.store( new
FileOutputStream(eeAlias + ".pfx" ), eePassword); |
1. 添加BCProvider
1
2
3 |
static
{ Security.addProvider( new
BouncyCastleProvider()); } |
2. 解除BCProvider密钥长度限制
下载对应版本jdk的jce不限密钥长度策略文件
Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 7
Java
Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files
6
解压后替换掉以下目录中的同名文件
$JRE_HOME/lib/security
$JAVA_HOME/jre/lib/security
3. 同一CA颁发的证书序列号一定不能相同。如果在项目中,证书序列号没有业务含义,则可使用uuid,如:
1
2 |
// 证书序列号 BigInteger serial = new
BigInteger(UUID.randomUUID().toString().replaceAll( "-" , "" ), 16 ); |
4. 当使用X500NameBuilder构建DN时,如果想控制生成的证书中的DN字段顺序,注意最终生成的DN与构建顺序相反
构建时DN字段顺序如下:
1
2
3
4
5
6
7
8 |
X500NameBuilder x500NameBuilder = new
X500NameBuilder(); x500NameBuilder.addRDN(BCStyle.C, "CN" ); x500NameBuilder.addRDN(BCStyle.ST, "Beijing" ); x500NameBuilder.addRDN(BCStyle.L, "Beijing" ); x500NameBuilder.addRDN(BCStyle.E, "daojoo@daojoo.com" ); x500NameBuilder.addRDN(BCStyle.O, "Daojoo.com Co.Ltd." ); x500NameBuilder.addRDN(BCStyle.OU, "Tech" ); x500NameBuilder.addRDN(BCStyle.CN, "daojoo" ); |
证书中展现的DN字段顺序如下:
1
2
3
4
5
6
7 |
CN = daojoo OU = Tech O = Daojoo.com Co.Ltd. E = daojoo@daojoo.com L = Beijing S = Beijing C = CN |
5. 使用keytool生成证书时,我没找到控制证书扩展项的参数。
?1. RFC2459
2. 公钥基础设施(PKI)实现和管理电子安全 清华大学出版社
3. The Cryptoworkshop Guide to Java Cryptography and the Bouncy Castle APIs
在JEE项目中实施SSL双向认证,布布扣,bubuko.com
标签:style class code java http tar
原文地址:http://www.cnblogs.com/daojoo/p/3784185.html