SSL证书结构
根证书的生成分为三个部分:
生成服务器证书
服务器证书的生成过程与根证书类似,只是给服务器原始证书签名的不是自己生成的私钥,而是根证书的私钥。
我们采用keytool生成相应的根秘钥库、服务器秘钥库、客户端秘钥库
1.从根秘钥库导出根原始证书;
2.从服务器秘钥库导出服务器原始证书;
3.从客户端秘钥库导出客户端原始证书;
4.制作根证书:采用java程序用根私钥对根原始证书签名,issuer与subject保持一致;
5.制作服务器证书:采用java程序用根私钥对服务器原始证书签名,subject的CN为服务器标识,表示专为某服务器签发;
6.制作客户端证书:采用java程序用根私钥对客户端原始证书签名,subject的CN为用户标识,表示专为某用户会话临时签发;
1生成根证书
keytool -list -v -keystore client20210427.keystore -storepass 'clientPassword'
1.1.生成秘钥
keytool -genkey -dname "CN=xx.domain.com, OU=domain, O=xx, L=bj, ST=cy, C=China" -alias root-alias -keypass 'rootPassword' -keyalg RSA -keystore root-key.store -validity 3600
1.2.生成根原始证书 service.cer
keytool -export -alias root-alias -storepass 'rootPassword' -file root-src.cer -keystore root-key.store
1.3.java程序用根私钥对原始证书进行签名
2生成服务器证书
2.1.生成服务器秘钥
keytool -genkey -dname "CN=xx.domain.com, OU=domain, O=xx, L=bj, ST=cy, C=China" -alias server-alias -keyalg RSA -keystore server-key.store -keypass 'serverPassword' -storepass 'serverPassword' -validity 1100
clientPassword
2.2.生成服务器原始证书 service.cer
keytool -export -alias server-alias -storepass 'serverPassword' -file server-src.cer -keystore server-key.store
2.3.java程序用根私钥对原始证书进行签名
3生成客户端证书
3.1.生成客户端秘钥
keytool -genkey -dname "CN=xx.domain.com, OU=domain, O=xx, L=bj, ST=cy, C=China" -alias client-alias -keyalg RSA -keystore client-key.store -keypass 'clientPassword' -storepass 'clientPassword' -validity 1100
3.2.生成客户端原始证书 service.cer
keytool -export -alias client-alias -storepass 'clientPassword' -file client-src.cer -keystore client-key.store
3.3.java程序用根私钥对原始证书进行签名
导出数字证书
keytool -exportcert -alias myCertificate -keystore myKeystore.keystore -file myCer.cer -rfc
各参数含义如下:
-exportcert 表示证书导出操作
-alias 指定别名
-keystore 指定密钥库文件
-file 指定导出证书的文件路径
-rfc 指定以Base64编码格式输出
打印证书
查看数字证书方法
keytool -printcert -file client.cer
再用openssl 验证一下是否正常
openssl x509 -inform DER -in root-sign.cer -text -noout
subject
C=China, ST=cy, L=bj, O=xx, OU=domain, CN=xx.domain.com
所有代码在jdk1.8下验证完成
/**
* 获取证书信息,并自签名待签证书 这里是自签名 得到根证书
*
* @param certPath : 待签证书地址
* @throws Exception
*/
public X509CertInfo createRootCert(String certPath) throws Exception {
// 获取待签名证书
FileInputStream is = new FileInputStream(certPath);
CertificateFactory vCertFactory = CertificateFactory.getInstance("X.509");
Certificate certificate = vCertFactory.generateCertificate(is);
is.close();
byte[] certData = certificate.getEncoded();
// 设置签名证书信息:有效日期、序列号、签名者、数字签名算发
X509CertImpl certImpl = new X509CertImpl(certData);
X509CertInfo cert = (X509CertInfo) certImpl.get(X509CertImpl.NAME + "." + X509CertImpl.INFO);
cert.set(X509CertInfo.VALIDITY, getCertValidity());
cert.set(X509CertInfo.SERIAL_NUMBER, getCertSerualNumber());
cert.set(X509CertInfo.ISSUER + "." + CertificateIssuerName.DN_NAME, cert.get(X509CertInfo.SUBJECT + "." + CertificateIssuerName.DN_NAME));
cert.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, getAlgorithm());
return cert;
}
签发
/**
* 取得待签证书信息,并签名待签证书
*
* @throws Exception
*/
public X509CertInfo signCert(CertInfo ci, X509CertInfo cert, String certSavePath) throws Exception {
X509CertImpl certImpl = new X509CertImpl(cert);
// 生成新正书验证码
certImpl.sign(ci.getPk(), "MD5WithRSA");
BASE64Encoder base64 = new BASE64Encoder();
File file = new File(certSavePath);
file.createNewFile();
FileOutputStream os = new FileOutputStream(certSavePath);
base64.encodeBuffer(certImpl.getEncoded(), os);
// certImpl.derEncode(os);
os.close();
return cert;
}
public X509CertInfo signCert2(CertInfo ci, X509CertInfo cert, String certSavePath) throws Exception {
X509CertImpl certImpl = new X509CertImpl(cert);
// 生成新正书验证码
certImpl.sign(ci.getPk(), "MD5WithRSA");
FileUtil.writeToFile(certSavePath, certImpl.getEncoded());
return cert;
}
public String signCertBase64(CertInfo ci, X509CertInfo cert) throws Exception {
X509CertImpl certImpl = new X509CertImpl(cert);
// 生成新正书验证码
certImpl.sign(ci.getPk(), "MD5WithRSA");
BASE64Encoder encoder = new BASE64Encoder();
String encoded = encoder.encode(certImpl.getEncoded());
StringBuilder sb = new StringBuilder();
sb.append("-----BEGIN RSA PRIVATE KEY-----\r\n");
sb.append(encoded);
sb.append("\r\n-----END RSA PRIVATE KEY-----");
return sb.toString();
}
创建证书
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.SUBJECT 证书使用者信息
/**
* 创建证书使用者信息
* @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;
}
对原始证书签名,并签入使用者信息,这里subject就是使用者信息,签名采用根证书私钥
public X509CertInfo createCert(String pin, InputStream is) throws Exception {
X500Name subject = createSubject(new SignInfo(), pin);
// 获取待签名证书
CertificateFactory vCertFactory = CertificateFactory.getInstance("X.509");
Certificate certificate = vCertFactory.generateCertificate(is);
is.close();
byte[] certData = certificate.getEncoded();
// 设置签名证书信息:有效日期、序列号、签名者、数字签名算发
X509CertImpl certImpl = new X509CertImpl(certData);
X509CertInfo cert = (X509CertInfo) certImpl.get(X509CertImpl.NAME + "." + X509CertImpl.INFO);
cert.set(X509CertInfo.VALIDITY, getCertValidity());
cert.set(X509CertInfo.SERIAL_NUMBER, getCertSerualNumber());
cert.set(X509CertInfo.ISSUER + "." + CertificateIssuerName.DN_NAME, cert.get(X509CertInfo.SUBJECT + "." + CertificateIssuerName.DN_NAME));
cert.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, getAlgorithm());
cert.set(X509CertInfo.SUBJECT, subject);
return cert;
}
maven 打包证书的过程中不断提示:IOException: Invalid keystore format
做以下两步操作即可解决
设置不替换的变量:p12 jks
这个p12是后缀的意思,不想让他去修改什么文件,就在这里面添加条目即可
org.apache.maven.plugins
maven-resources-plugin
p12
jks
这样签发的证书在linux完美通过验证,but在windows上不能通过验证,于是我们对linux和windows上的证书格式进行比较,发现windows上根证书必须要包含X509v3 Basic Constraints,CA:True
private CertificateExtensions createCertificateExtensions2() throws IOException {
CertificateExtensions exts = new CertificateExtensions();
BasicConstraintsExtension bce = new BasicConstraintsExtension(true, true, -1);
exts.set(BasicConstraintsExtension.NAME, new BasicConstraintsExtension(false, bce.getExtensionValue()));
return exts;
}
//对根证书签名代码增加如下一行,写入extension
cert.set(X509CertInfo.EXTENSIONS, extensions);
参考
https://www.jianshu.com/p/ed6486f0e608
https://blog.csdn.net/JustinQin/article/details/100052500
https://blog.csdn.net/jxctx/article/details/45970199