JAVA SM2 数字证书生成

1 篇文章 0 订阅
1 篇文章 0 订阅

Before Start

X.509数字证书请参考:

RFC5280 Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile

中文版的简要介绍可以参考这篇文章 Agzs . X509证书–ANS1结构

如果还未能生成SM2密钥对请先阅读

Build with Maven

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.60</version>
</dependency>

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk15on</artifactId>
    <version>1.60</version>
</dependency>

QuickStart

Github

/**
 * BouncyCastle算法提供者
 */
private static final Provider BC = new BouncyCastleProvider();

生成自签名公私钥对

keyPairGenerator的构造请参考 JAVA SM2 密钥生成 签名验签

// 产生密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();

证书签名算法算法提供者

在制作证书时需要使用到签名算法签名证书中部分数据区域,国密类型的数字证书使用的签名算法是SM3withSM2,这里使用私钥创建算法提供容器。

ContentSigner sigGen = new JcaContentSignerBuilder("SM3withSM2")
        .setProvider(BC)
        .build(keyPair.getPrivate());

设置证书信息

设置证书的基本数据:使用者信息、颁发者信息、证书序号、证书生效日期、证书失效日期,以及证书扩展属性。

标识信息构造(DN)

上面提到的使用者信息、颁发者信息,使用Distinct Name的方式来描述。

关于DN中的各个字段的含义请参考 IBM Previous Next
Distinguished Names

实际上简单来讲就是,用来确定“实体” 身份/信息 的一系列列键值对组成的字符串。

这里的键是一个ASN1ObjectIdentifier,实际上Bouncycastle已经为我们把需要的大多键都已经列好了,我们只要使用这个类org.bouncycastle.asn1.x500.style.BCStyle的静态变量就可以。

private static X500NameBuilder createStdBuilder() {
    X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
    // 国家代码
    builder.addRDN(BCStyle.C, "CN");
    // 组织
    builder.addRDN(BCStyle.O, "HZNU");
    // 省份
    builder.addRDN(BCStyle.ST, "Zhejiang");
    // 地区
    builder.addRDN(BCStyle.L, "Hangzhou");
    return builder;
}

然后我们就可以使用X500NameBuilderbuild()方法构造对应的DN了。

BCStyle 源码请参考,也可以使用与该文件中处于同级目录的RFC4519Style

获取扩展密钥用途构造(可选)

如果需要设置证书的扩展密钥用途,可以使用DERSequence来构造一个拓展密钥用途的序列。

拓展密钥用途

public static DERSequence extendedKeyUsage() {
        // 构造容器对象
        ASN1EncodableVector vector = new ASN1EncodableVector();
        // 客户端身份认证
        vector.add(KeyPurposeId.id_kp_clientAuth);
        // 安全电子邮件
        vector.add(KeyPurposeId.id_kp_emailProtection);
        return new DERSequence(vector);
}

扩展密钥用途的各个OID Bouncycastle 已经为我们提供,请参考KeyPurposeId

证书信息构造

接下来可以使用X509v3CertificateBuilder 来设置证书的基本参数,下面列举基本一些证书参数和扩展参数的设置方式。

// 构造X.509 第3版的证书构建者
X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(
        // 颁发者信息
        createStdBuilder().build()
        // 证书序列号
        , BigInteger.valueOf(1)
        // 证书生效日期
        , new Date(System.currentTimeMillis() - 50 * 1000)
        // 证书失效日期
        , new Date(System.currentTimeMillis() + 50 * 1000)
        // 使用者信息(PS:由于是自签证书,所以颁发者和使用者DN都相同)
        , createStdBuilder().build()
        // 证书公钥
        , keyPair.getPublic())
        /*
        设置证书扩展
        证书扩展属性,请根据需求设定,参数请参考 《RFC 5280》
         */
        // 设置密钥用法
        .addExtension(Extension.keyUsage, false
                , new X509KeyUsage(X509KeyUsage.digitalSignature | X509KeyUsage.nonRepudiation))
        // 设置扩展密钥用法:客户端身份认证、安全电子邮件
        .addExtension(Extension.extendedKeyUsage, false, extendedKeyUsage())
        // 基础约束,标识是否是CA证书,这里false标识为实体证书
        .addExtension(Extension.basicConstraints, false, new BasicConstraints(false))
        // Netscape Cert Type SSL客户端身份认证
        .addExtension(MiscObjectIdentifiers.netscapeCertType, false, new NetscapeCertType(NetscapeCertType.sslClient));

更多 Netscape Cert Type 类型请参考:NetscapeCertType

X.509格式证书对象生成

通过使用证书构造好的信息,接下就能够生成x509格式的证书对象

// 将证书构造参数装换为X.509证书对象
X509Certificate certificate = new JcaX509CertificateConverter()
        .setProvider(BC)
        .getCertificate(certGen.build(sigGen));
保存证书

如果需要永久的保存刚才生成的这份证书,那么我们需要对这个证书进行序列化,常见的证书序列化是将ASN.1编码的证书对象,进行BASE64编码,这样证书更加便于网络传输。

public static void makeCertFile(X509Certificate x509Certificate, Path savePath) throws CertificateEncodingException, IOException {
    if (Files.exists(savePath)) {
        // 删除已存在文件
        Files.deleteIfExists(savePath);
    }
    // 创建文件
    Files.createFile(savePath);
    // 获取ASN.1编码的证书字节码
    byte[] asn1BinCert = x509Certificate.getEncoded();
    // 编码为BASE64 便于传输
    byte[] base64EncodedCert = Base64.encode(asn1BinCert);
    // 写入文件
    Files.write(savePath, base64EncodedCert);
}

给与文件名称,调用保存(常见的DER编码的证书后缀名为.cer)。

// 保存为证书文件
makeCertFile(certificate, Paths.get("test-cert.cer"));

生成的证书截图

ASN.1 结构解析工具

asn1js

通过该工具,输入证书的BASE64编码的字符串,我们能够快速分析证书的结构,以及各种字段。
截图

致谢

在编写代码时参考了下面的例子:

参考列表:

  • 6
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值