Java使用BouncyCastle进行基于ECDSA算法的椭圆曲线secp256r1证书自签名

一、    基础
        BouncyCastle,是一个用于Java平台的轻量级开源密码包。目前除了支持Java外,也支持C#。授权遵循MIT协议。
        BouncyCastle几乎完成了当前主流的所有加解密算法、证书生成签发等内容。作为JCE(Java Cryptography Extension)的提供者(Provider),BouncyCastle对如消息摘要处理、非对称加密密钥对生成、证书签名处理等都提供了良好的支持。
二、    关键词
        ECDSA,椭圆曲线数字签名算法(ECDSA)是使用椭圆曲线密码(ECC)对数字签名算法(DSA)的模拟。具体算法不在此描述。
        secp256r1,可以简单理解为一种ECC,或者就简单理解为一种曲线。
        非对称加密算法,可以理解为通信的双方,各自有一套密钥对。即公钥和私钥。私钥自己保存,公钥可公开。在双方通信时,一方先使用对方的公钥对数据加密,另一方在收到数据后,用自己的私钥解密数据。
        数字证书,包含公钥、主体信息、扩展部分等诸多内容,最后使用签名算法,用私钥对这些信息进行签名。在双方通信过程中,通过数字证书的传递,可以理解为识别对方主体信息、并交换了公钥,及对其中的签名进行认证(判断可信度)。
CA,即电子商务认证中心,可以理解为一个权威的第三方机构。假设一种情景,如果两人交互,在彼此不认识的情况下,又无法识别对方的证件真伪时,一般是很难互相取得信任的。但是,如果存在双方都信任的第三个人(中间人),这个事情就好办多了。CA就类似于这个中间人。CA会对双方的证书进行签名,这样在一方与另一方通信时,只要携带被共同信任的CA签名过的证书,就可以认为这方可以信任。
        CSR,即证书签名请求,包含自身的公钥、主体信息及与公钥对应的私钥自签名。一方可向CA机构提出证书签名请求,从而获取经CA签名过的数字证书。
三、    自签名步骤
        一个自签名的步骤可按照如下顺序进行:
(1)    生成自签名根CA证书(含证书及私钥)
(2)    生成待签名主体的密钥对(含公钥及私钥)
(3)    生成待签名主体的证书签名请求(CSR)
(4)    解析CSR,并根据自签名根CA证书和其对应的私钥,对待签名主体进行签名
四、    生成自签名根CA证书
        这个步骤可以使用openssl进行,如在安装有openssl的电脑上,用命令行工具执行以下脚本:

openssl ecparam -genkey -name secp256r1 | openssl ec -out privateKeys\oemRootCA.key -aes128 -passout file:passphrase.txt
openssl req -new -x509 -days 36500 -sha256 -key privateKeys\oemRootCA.key -set_serial 05 -passin file:passphrase.txt -config configs\oemRootCACert.cnf -extensions ext -out certs\oemRootCA.pem

        第一句使用ecparam,设置ec(用椭圆曲线算法生成密钥)时使用的曲线为secp256r1,接下来,使用ec命令,生成私钥。并使用128位AES对这个私钥的内容加密。
        第二句提出证书签名请求,并使用SHA256算法进行签名,生成证书为X.509格式的。
经过这命令后,可生成根CA证书及私钥文件。

 五、    生成待签名主体的密钥对
        有了根CA证书(虽然是自签名的,但也没关系),我们就可以在Java中使用BouncyCastle库,生成密钥对了。
        在使用BouncyCastle前,首先需要将BouncyCastle加入到Security的提供者中:

Security.addProvider(new BouncyCastleProvider());

        这样,以后在使用BouncyCastle时,提供者就可以使用名称“BC”获取。比如:

provider = Security.getProvider("BC");

        默认情况下,虚拟机会使用Java自带的默认提供者进行签名等相关操作,但是自带的提供者库是不支持很多新的算法和曲线的。
        之后,可如下生成密钥对:

// 获取椭圆曲线签名算法ECDSA生成器
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA", provider);
// 设置椭圆曲线参数
ECGenParameterSpec ecSpec = new ECGenParameterSpec(ecName);
// 生成密钥对
KeyPair pair = keyGen.initialize(ecSpec);

六、    生成证书签名请求
        在生成证书签名请求之前,还需要准备几个内容:主体信息、扩展信息等。
(1)    主体信息
        主体信息(Subject),一般包含了ASN1定义的一些内容,如:CN,commonName代表公用名;O,organization代表组织;OU,organizationUnit代表组织单位,等等。
        在Java中,我们使用X500Name构建这个信息:

X500NameBuilder builder = new X500NameBuilder(X500Name.getDefaultStyle());
builder.addRDN(BCStyle.C, country);
builder.addRDN(BCStyle.O, organization);
builder.addRDN(BCStyle.OU, organizationalUnit);
builder.addRDN(BCStyle.CN, commonName);
builder.addRDN(BCStyle.DC, domainComponent);
builder.build();

(2)    扩展信息
        扩展信息(Extensions),一般包含了ASN1定义的一些证书次要内容,如:basicConstraints、keyUsage等。
        这里,我们使用一个列表,构建Extension的集合:

List<Extension> extensions = new ArrayList<Extension>();
BasicConstraints basicConstraints = new BasicConstraints(isCA);
Extension extension = new Extension(Extension.basicConstraints, true, new DEROctetString(basicConstraints));
extensions.add(extension);
KeyUsage keys = new KeyUsage(keyUsage);
extension = new Extension(Extension.keyUsage, keyUsageCritical, new DEROctetString(keys));
extensions.add(extension);

(3)    构建证书签名请求
        证书签名请求实际上是使用待签名主体的私钥,对其主体信息及公钥信息进行自签名的过程:

PKCS10CertificationRequestBuilder builder = new PKCS10CertificationRequestBuilder(subject, publicKeyInfo);
if (extensions != null && extensions.size() > 0) 
{
	for (Extension extension : extensions) 
	{
		if (extension == null) {
			continue;
		}
		builder.addAttribute(extension.getExtnId(), extension.getParsedValue());
	}
}
ContentSigner signer = null;
PKCS10CertificationRequest csr = null;
try {
	//获取签名信息
	signer = getContentSigner(privateKey, signatureAlgorithm);
	//构建证书签名请求
	csr = builder.build(signer);
}catch(IOException|OperatorCreationException e)
{
	//获取签名信息失败
	e.printStackTrace();
}

        在签名前,我们需要准备好主体信息,及公钥信息。并采用指定的签名算法,对内容进行签名。签名后得到的就是证书签名请求文件了。
        证书签名请求文件,包含了待签名主体的信息、待签名主体的公钥及用与公钥对应的私钥进行的签名。
七、    签发证书
        这一步,我们使用先前生成的根CA证书及其对应私钥,对证书请求文件进行签名。
        首先,我们需要通过CA证书,得到CA的主体信息,这个信息以后会关联到最后签发后的证书中的父级主题中。

X500Principal principal = certIssuer.getSubjectX500Principal(); 
X500Name parentSubjectDn = X500Name.getInstance(principal.getEncoded());

        这里的certIssuer就是X509格式证书,即X509Certificate。
        接下来,我们就可以签发证书了:

//初始化证书的序列号
BigInteger serial = BigInteger.probablePrime(32, new Random());
//日期转化
Date notBeforeDate = Date.from(notBefore.atZone(ZoneId.systemDefault()).toInstant());
Date notAfterDate = Date.from(notAfter.atZone(ZoneId.systemDefault()).toInstant());
//证书签名构建器
X509v3CertificateBuilder builder = new X509v3CertificateBuilder(issuerDn, serial, notBeforeDate, notAfterDate, csr.getSubject(), csr.getSubjectPublicKeyInfo());
if (extensions != null && extensions.size() > 0) 
{
	for (Extension extension : extensions) {
	    if (extension == null) {
		    continue;
	    }
	    builder.addExtension(extension.getExtnId(), extension.isCritical(), extension.getParsedValue());
    }
    JcaX509ExtensionUtils sdf = new JcaX509ExtensionUtils();
    SubjectKeyIdentifier subjectKeyIdentifier =  sdf.createSubjectKeyIdentifier(csr.getSubjectPublicKeyInfo());
    builder.addExtension(Extension.subjectKeyIdentifier, false, subjectKeyIdentifier);
}
//获取签名(签发证书时,使用CA的私钥来签名)
ContentSigner signer = getContentSigner(issuerPrivateKey, signatureAlgorithm);
X509CertificateHolder holder = builder.build(signer);
// BC加密算法
X509Certificate cert = new JcaX509CertificateConverter().setProvider(provider).getCertificate(holder);
//验证用于签名证书的私钥是否与传入的公钥是一对
cert.verify(issuerPublicKey);

        这个步骤,我们构建了证书的序列号、证书的有效期(notBefore及notAfter)。并根据证书签名请求文件,构建了X509证书构建器。
        和先前一样,我们提供了待签名的主体信息。
        以上这些作为待签名的内容,最后使用CA根证书的私钥及指定的签名算法SHA256,生成签名后的证书。这个证书就是颁发给待签名主体的证书了。
八、    工具包
        最后的最后,为了方便使用,我编写了BouncyCastleUtility工具包,封装了此过程中的零碎操作。有需要的小伙伴可以前往下载。地址Java平台BouncyCastleUtility工具包-Java文档类资源-CSDN下载

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值