标签: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.comkeytool -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 = daojooOU = TechO = Daojoo.com Co.Ltd.E = daojoo@daojoo.comL = BeijingS = BeijingC = 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