Java管理SSL证书

SSL证书结构

根证书的生成分为三个部分:

  • 生成根证书的公钥和私钥
  • 构建证书的原始证书,包括公钥、加密算法、生效时间、过期时间等信息。
  • 用根证书私钥对原始证书进行签名,生成合法的根证书。
  • 根证书安装,将根证书安装到客户端,比如手机、电脑中。
    代码

 

           

 

生成服务器证书

      

         

 

服务器证书的生成过程与根证书类似,只是给服务器原始证书签名的不是自己生成的私钥,而是根证书的私钥。

客户端证书校验过程

  • 客户端收到服务端发送的服务器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是后缀的意思,不想让他去修改什么文件,就在这里面添加条目即可

<plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-resources-plugin</artifactId>
     <configuration>
         <nonFilteredFileExtensions>
              <nonFilteredFileExtension>p12</nonFilteredFileExtension>
              <nonFilteredFileExtension>jks</nonFilteredFileExtension>
         </nonFilteredFileExtensions>
     </configuration>
</plugin>

 

这样签发的证书在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

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码者人生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值