1.引言
CA是Certificate Authority的缩写,也叫“证书授权中心”。它是负责管理和签发证书的第三方机构,作用是检查证书持有者身份的合法性,并签发证书,以防证书被伪造或篡改。
CA证书即由CA所签发的证书。
SSL双向认证需要服务端与客户端提供身份认证,只能是服务端允许的客户能去访问,安全性相对于要高一些。就是只有安装了由CA证书颁发的用户证书的机器才能进行系统的访问。
2.实现配置
接上一遍博文:HTTPS从了解到掌握(一):证书生成及启用HTTPS 单向认证中的Nginx配置,一般配置都使用购买得到的证书文件,除此之外需要追加两个配置项
ssl_verify_client on; #开户客户端证书验证
ssl_client_certificate /Users/kirra/Desktop/test.pem; #可以验证签发过的客户端证书,一般配置根证书
3.用户证书生成代码详解
3.1.KeyStore
首先我们看一下证书存储对象KeyStore
/**
* This class represents a storage facility for cryptographic
* keys and certificates
**/
其中类方法setKeyEntry(String alias, Key key, char[] password, Certificate[] chain),定义证书相关的信息
/**
* @param alias the alias name
* @param key the key to be associated with the alias
* @param password the password to protect the key
* @param chain the certificate chain for the corresponding public
**/
3.2.生成证书参数
在方法中我们有个步骤是必须做的,就是先引入Bouncy Castle:Bouncy Castle(轻量级密码术包)是一种用于 Java 平台的开放源码的轻量级密码术包;它支持大量的密码术算法,并提供JCE 1.2.1的实现。为了启用它,在Maven项目我们需要引用bcprov-jdk15on,并且动态加入。
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.65</version>
</dependency>
static {
Security.addProvider(new BouncyCastleProvider());
}
使用xca不能生成priavte文件,因此我们需要通过可生成的文件转化为priavte文件类,因此有
public static PrivateKey generatePrivate(String pemfilesourceurl)
{
try
{
//使用PemReader类读取
PemReader pemReader = new PemReader(new InputStreamReader(new ClassPathResource(pemfilesourceurl).getInputStream()));
//获取encoded的pem信息
PemObject pemObject = pemReader.readPemObject();
byte[] pemcontent = pemObject.getContent();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//当证书提供方提供的是一个pem文件时候如何通过java生成对应的PrivateKey类
return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(pemcontent));
}
catch (Exception ex)
{
ex.printStackTrace();
return null;
}
}
为了生成证书链路Certificate[] chain,我们需要使用到X509CertInfo,证书的主体信息由该类进行封装
X509CertInfo info = new X509CertInfo();
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(new java.util.Random().nextInt() & 0x7fffffff));
info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(AlgorithmId.get("SHA256withRSA")));
info.set(X509CertInfo.SUBJECT, subject);
info.set(X509CertInfo.KEY, new CertificateX509Key(subjectKeyPair.getPublic()));
info.set(X509CertInfo.VALIDITY, validity);
info.set(X509CertInfo.ISSUER, x509Certificate.getIssuerDN());
各信息为:X509CertInfo.VERSION 证书版本号
X509CertInfo.SERIAL_NUMBER 证书序列号
X509CertInfo.ALGORITHM_ID 签名算法
X509CertInfo.SUBJECT 证书使用者信息,由工具方法createSubject来生成,其中信息可以根据实际需要进行自定义
/**
* 创建证书使用者信息
* @param CN comonName
* @param OU organizationalUnitName
* @param O organizationName
* @param C countryName
* @param L localityName
* @param ST stateOrProvinceName
* @return
* @throws IOException
*/
public static X500Name createSubject(String CN,String OU,String O,String C,String L,String ST) throws IOException {
X500Name subject = new X500Name(new StringBuilder()
.append("CN=").append(CN)
.append(",OU=").append(OU)
.append(",O=").append(O)
.append(",C=").append(C)
.append(",L=").append(L)
.append(",ST=").append("abc").toString());
return subject;
}
X509CertInfo.KEY 公钥算法
X509CertInfo.VALIDITY 有效期限,由工具方法createValidity来生成,其中信息可以根据实际需要进行自定义
public static CertificateValidity createValidity(Integer startYear, Integer blank) throws Exception {
if(blank<1)
throw new Exception("间隔不能少于1");
Calendar calendar = Calendar.getInstance();
calendar.set(startYear,0,1,0,0);
Date firstDate = calendar.getTime();
calendar.set(startYear+blank,0,1,0,0);
Date lastDate = calendar.getTime();
return new CertificateValidity(firstDate, lastDate);
}
X509CertInfo.ISSUER 颁发者,颁发者必须与CA证书信息一致才能通过双向认证,因此该参数由CA证书提供
3.3 输出用户证书
利用CA证书对用户证书签名后就可以输出用户证书文件
X509Certificate certificate = (X509Certificate) cert;
X509Certificate[] X509Certificates = new X509Certificate[]{certificate};
// 生成用户安装证书并输出到指定路径
KeyStore keyStore = KeyStore.getInstance("pkcs12");
keyStore.load(null, null);
keyStore.setKeyEntry("test",
subjectKeyPair.getPrivate(),
"123456".toCharArray(),
X509Certificates);
File file = new File(userPFXfilePath);
if (file.exists()) file.delete();
FileOutputStream fos = new FileOutputStream(userPFXfilePath);
keyStore.store(fos, "123456".toCharArray());
fos.close();
4.结果
4.1 选用证书
wo
4.2 不选用证书
参考:1.SSL双向认证和SSL单向认证的区别
GitHub:https://github.com/tale2009/nginx-using/tree/master/CA-Demo