java生成国密sm2数字证书

743人浏览 / 0人评论

一、数字证书

        数字证书是一段经CA签名的、包含拥有者身份信息和公开密钥的数据体。数字证书和一对公、私钥相对应。公钥以明文形式放到数字证书中,私钥为拥有者秘密掌握。CA确保数字证书中信息的真实性,可以作为终端实体的身份证明。在电子商务和网络信息交流中,数字证书常用来解决相互间的信任问题。可以说,数字证书类似于现实生活中公安部门发放的居民身份证。

        国际标准X.509定义了规范的数字证书格式,它是PKI技术体系中应用最广泛、最基础的一个国际标准。许多与PKI相关的协议标准(如PKIX、S/MIME、SSL、TLS、IPsec等)都是在X.509基础上发展起来的。

二、国密数字证书的基本数据结构

        以下是国密SM2证书的ASN.1结构定义,与国际上RSA证书采用的X509格式差别不大。

Certificate ::= SEQUENCE {
    tbsCertificate       TBSCertificate, -- 证书主体
    signatureAlgorithm   AlgorithmIdentifier, -- 证书签名算法标识
    signatureValue       BIT STRING --证书签名值
}

TBSCertificate ::= SEQUENCE {
    version         [0]  EXPLICIT Version DEFAULT v1, -- 证书版本号
    serialNumber         CertificateSerialNumber, -- 证书序列号(对同一CA所颁发的证书,序列号唯一标识证书)
    signature            AlgorithmIdentifier, --证书签名算法标识
    issuer               Name,                --证书发行者名称
    validity             Validity,            --证书有效期
    subject              Name,                --证书主体名称
    subjectPublicKeyInfo SubjectPublicKeyInfo,--证书公钥
    issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,-- 证书发行者ID(可选),只在v2、v3中支持
    subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,-- 证书主体ID(可选),只在v2、v3中支持
    extensions      [3]  EXPLICIT Extensions OPTIONAL -- 证书扩展段(可选),只在v3中支持
}

Version ::= INTEGER { v1(0), v2(1), v3(2) }

CertificateSerialNumber ::= INTEGER

AlgorithmIdentifier ::= SEQUENCE {
    algorithm        OBJECT IDENTIFIER,
    parameters       ANY DEFINED BY algorithm OPTIONAL
}

Name ::= CHOICE { RDNSequence }

RDNSequence ::= SEQUENCE OF RelativeDistinguishedName

RelativeDistinguishedName ::= SET OF AttributeTypeAndValue

AttributeTypeAndValue ::= SEQUENCE {
    type     AttributeType,
    value    AttributeValue
}

AttributeType ::= OBJECT IDENTIFIER

AttributeValue ::= ANY DEFINED BY AttributeType

Validity ::= SEQUENCE {
    notBefore      Time, -- 证书有效期起始时间
    notAfter       Time  -- 证书有效期终止时间
}

Time ::= CHOICE {
    utcTime        UTCTime, -- 世界时间类型
    generalTime    GeneralizedTime -- 通用时间类型
}

SubjectPublicKeyInfo ::= SEQUENCE {
    algorithm            AlgorithmIdentifier, -- 公钥算法
    subjectPublicKey     BIT STRING           -- 公钥值
}

UniqueIdentifier ::= BIT STRING

Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension

Extension ::= SEQUENCE {
    extnID      OBJECT IDENTIFIER,
    critical    BOOLEAN DEFAULT FALSE,
    extnValue   OCTET STRING 
}

三、生成国密SM2数字证书

        下面采用开源BC库生成国密SM2的数字证书,生成PFX格式和cert格式的证书。其中cert证书只包含公钥证书,PFX证书既包含公钥也包含私钥。

Maven依赖:

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

演示代码:

@Slf4j
public class SM2CertTest extends SM2Test {

	private X500Name createX500Name(String country, String province, String locality, String organization,
			String commonName) {
		X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
		// 通用名称
		builder.addRDN(BCStyle.CN, commonName);
		// 组织
		builder.addRDN(BCStyle.O, organization);
		// 地区
		builder.addRDN(BCStyle.L, locality);
		// 省份或州
		builder.addRDN(BCStyle.ST, province);
		// 国家代码
		builder.addRDN(BCStyle.C, country);
		return builder.build();
	}

	private void addExtensions(X509v3CertificateBuilder certBuilder) throws Exception {
		certBuilder
				// 设置密钥用法
				.addExtension(Extension.keyUsage, false, new X509KeyUsage(X509KeyUsage.digitalSignature | X509KeyUsage.nonRepudiation))
				// 设置扩展密钥用法:客户端身份认证、安全电子邮件
				.addExtension(Extension.extendedKeyUsage, false, this.getExtendedKeyUsage())
				// 基础约束,标识是否是CA证书,这里false标识为实体证书
				.addExtension(Extension.basicConstraints, false, new BasicConstraints(false))
				// SSL客户端身份认证
				.addExtension(MiscObjectIdentifiers.netscapeCertType, false, new NetscapeCertType(NetscapeCertType.sslClient))
				// 使用者备用名称
				.addExtension(Extension.subjectAlternativeName, false, this.getSubjectAlternativeName());
	}

	private DERSequence getExtendedKeyUsage() {
		ASN1EncodableVector v = new ASN1EncodableVector();
		// 客户端身份认证
		v.add(KeyPurposeId.id_kp_clientAuth);
		// 安全电子邮件
		v.add(KeyPurposeId.id_kp_emailProtection);
		return new DERSequence(v);
	}

	private DERSequence getSubjectAlternativeName() {
		// 作为SSL证书时的域名绑定
		ASN1EncodableVector v = new ASN1EncodableVector();
		v.add(new GeneralName(GeneralName.dNSName, "baidu.com"));
		v.add(new GeneralName(GeneralName.dNSName, "www.baidu.com"));
		v.add(new GeneralName(GeneralName.dNSName, "*.baidu.com"));
		return new DERSequence(v);
	}

	private X509Certificate createCert(X500Name issuer, X500Name subject, PrivateKey privateKey, PublicKey publicKey)
			throws Exception {
		SerialNumber sn = new SerialNumber(new BigInteger(DateUtil.format(new Date(), "yyyyMMddHHmmss")
				+ DateUtil.currentSeconds() + System.currentTimeMillis()));
		// 构造X.509 第3版的证书构建者
		X509v3CertificateBuilder tbsBuilder = new JcaX509v3CertificateBuilder(
				// 颁发者信息
				issuer
				// 证书序列号
				, sn.getNumber()
				// 证书生效日期
				, new Date(DateUtil.offsetMonth(new Date(), -1).getTime())
				// 证书失效日期
				, new Date(DateUtil.offsetMonth(new Date(), 240).getTime())
				// 使用者信息(PS:由于是自签证书,所以颁发者和使用者DN都相同)
				, subject
				// 证书公钥
				, publicKey);
		// 设置证书扩展
		this.addExtensions(tbsBuilder);
		// 证书签名
		ContentSigner signer = new JcaContentSignerBuilder("SM3withSM2").setProvider("BC").build(privateKey);
		// 将证书构造参数装换为X.509证书对象
		X509Certificate certificate = new JcaX509CertificateConverter().setProvider("BC")
				.getCertificate(tbsBuilder.build(signer));
		return certificate;
	}

	private void savePfx(X509Certificate certificate, PrivateKey privateKey, String filename, String password)
			throws Exception {
		// 生成pfx文件
		String pfxPath = "C:/" + filename + ".pfx";
		KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
		log.info("KeyStore类型:{}", keyStore.getType());
		keyStore.load(null, null);
		String alias = IdUtil.fastSimpleUUID();
		keyStore.setKeyEntry(alias, privateKey, password.toCharArray(), new X509Certificate[] { certificate });
		FileOutputStream fos = new FileOutputStream(new File(pfxPath));
		keyStore.store(fos, password.toCharArray());
		fos.flush();
	}

	private void saveCer(X509Certificate certificate, String filename) throws Exception {
		// 生成cer文件
		String cerPath = "C:/" + filename + ".cer";
		log.info("证书数据: {}", HexUtil.encodeHexStr(certificate.getEncoded()));
		FileUtil.writeBytes(certificate.getEncoded(), cerPath);
	}

	@Test
	public void testCreateCert() throws Exception {
		// 产生CA数字证书和PFX
		KeyPair caKeyPair = getSM2KeyPair();
		X500Name caX500Name = createX500Name("CN", "CS", "CD", "HD", "CA_TEST");
		X509Certificate caCertificate = createCert(caX500Name, caX500Name, caKeyPair.getPrivate(),
				caKeyPair.getPublic());
		String filename = IdUtil.fastSimpleUUID() + "_sm2-ca";
		saveCer(caCertificate, filename);
		savePfx(caCertificate, caKeyPair.getPrivate(), filename, "111111");
	}

}

执行结果如下:

14:37:45.282 [main] INFO org.wcls.cryptotest.SM2Test - 私钥:308193020100301306072a8648ce3d020106082a811ccf5501822d04793077020101042059c9fab82e7f454beca0927fa1018b12ea262c2845b1f4e6b48aba49d64ba836a00a06082a811ccf5501822da14403420004ce4fe174c8851d1aacead11d972b3e26010e1016c4216dc24e8ac8c5922800de270f4ecd3eed379f73e48ba514ccc54d55b0f5d5e112c168737aa3e51fd38280
14:37:45.315 [main] INFO org.wcls.cryptotest.SM2Test - 公钥:3059301306072a8648ce3d020106082a811ccf5501822d03420004ce4fe174c8851d1aacead11d972b3e26010e1016c4216dc24e8ac8c5922800de270f4ecd3eed379f73e48ba514ccc54d55b0f5d5e112c168737aa3e51fd38280
14:37:45.707 [main] INFO org.wcls.cryptotest.SM2CertTest - 证书数据: 308201d53082017ca003020102021001850b34206c3df9f47850b3bc5c6859300a06082a811ccf5501837530463110300e06035504030c0743415f54455354310b3009060355040a0c024844310b300906035504070c024344310b300906035504080c024353310b300906035504061302434e301e170d3230303230373036333734355a170d3430303330373036333734355a30463110300e06035504030c0743415f54455354310b3009060355040a0c024844310b300906035504070c024344310b300906035504080c024353310b300906035504061302434e3059301306072a8648ce3d020106082a811ccf5501822d03420004ce4fe174c8851d1aacead11d972b3e26010e1016c4216dc24e8ac8c5922800de270f4ecd3eed379f73e48ba514ccc54d55b0f5d5e112c168737aa3e51fd38280a34c304a300b0603551d0f0404030206c0301d0603551d250416301406082b0601050507030206082b0601050507030430090603551d1304023000301106096086480186f8420101040403020780300a06082a811ccf55018375034700304402203c46862a16f97ebae38981946e894589f32a107ddde9fd05bf2e33dc4690b499022038475a179ea6d104d4a48953f8ad7592ee48afd3d5254a30ed851704ee5f1b45
14:37:45.808 [main] INFO org.wcls.cryptotest.SM2CertTest - KeyStore类型:PKCS12

 

参考资料

[1] GB/T 20518-2018 信息安全技术 公钥基础设施 数字证书格式

[2] GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范

全部评论