使用bouncycastle X509v3CertificateBuilder生成X509Certificate [英] Generating X509Certificate using bouncycastle X509v3CertificateBuilder

查看:318
本文介绍了使用bouncycastle X509v3CertificateBuilder生成X509Certificate的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图将JXTA移植到App Engine上运行。考虑到App Engine尚不支持BouncyCastleBC提供程序,我必须移植现有的JXTA代码以使用白名单类生成X509Certificate。我对Crypto的了解很少,我不确定我想要完成的事情甚至是可能的。以下是来自JXTA项目的PSEUtils.java的原始代码:

PSEUtils.java

有一个包含 java.security.cert.X509Certificate 的助手类:

  public静态类IssuerInfo {
public X509Certificate cert; //主题证书
public PrivateKey subjectPkey; //主题私钥
public X509Certificate颁发者; //发行者Cert
public PrivateKey issuerPkey; //发行者私钥
}

在方法中:

  public static IssuerInfo genCert(X500Principal subject,KeyPair keypair,IssuerInfo issuerinfo)

我传入主题为:

  new X500Principal(CN =+使用CN)

keypair as(from original code):

  KeyPairGenerator g = KeyPairGenerator.getInstance(RSA); 
g.initialize(1024,UTILS.srng);
KeyPair keypair = g.generateKeyPair();

和jxta编码的IssuerInfo。

现在,由于我无法使用bouncycastle.jce包,因此我必须删除JXTA使用的X509Principal和X509V3CertificateGenerator代码,并尝试用符合GAE限制的实现来替换它。下面是我现在使用 org.bouncycastle.X509.X509v3CertificateBuilder 的genCert方法。

  SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo.getInstance(keypair.getPublic()。getEncoded()); 

X509v3CertificateBuilder v3CertGen = new X509v3CertificateBuilder(
new X500Name(issuer.getName()),
BigInteger.ONE,
今天直到
new X500Name (subject.getName()),
subPubKeyInfo);

问题是我无法获得 keypair.getPublic()。 getEncoded()使用 SubjectPublicKeyInfo.getInstance()方法。在工厂中抛出 java.lang.IllegalArgumentException:未知对象:[B



公钥似乎在检查时被填充:



'pre> 孙RSA公钥,1024位
模量:117521430893506212334140912845641570591161279468597426442875306202350445904550279678434051874985419676760802566018092318362676224355315431299979507080364677679613392086245588766565617009250512996843008784370448997729071786062596049780632058501646041736216482596596901215941577208285499619376322050871534546271
公共指数:65537

我发现了下面的SO链接,它演示了这段代码的工作原理:

< a href =https://stackoverflow.com/questions/7230330/sign-csr-using-bouncy-castle>使用Bouncy Castle签署CSR



我尝试转换genCert在下面,但由于某些原因,我无法通过编码公钥创建SubjectPublicKeyInfo?



任何帮助都非常感谢。

  public static IssuerInfo genCert X500Principal主题,KeyPair密钥对,IssuerInfo issuerinfo){
IssuerInfo info = new IssuerInfo();
尝试{
//设置发行者
PrivateKey签名者;
X500Principal发行人;

if(null == issuerinfo){//自签名根证书
签名者= keypair.getPrivate();
issuer = new X500Principal(subject.getEncoded());
} else {//颁发者签署的服务请求
signer = issuerinfo.subjectPkey;
X500Principal issuer_subject = issuerinfo.cert.getSubjectX500Principal();
issuer = new X500Principal(issuer_subject.getEncoded());
}

//设置有效期从今天起10年
今日日期=新日期();
日历cal = Calendar.getInstance();

cal.setTime(今天);
cal.add(Calendar.YEAR,10);
直到= cal.getTime();

SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo.getInstance(keypair.getPublic()。getEncoded());

// **无法在这里得到,所以我不确定其余的工作是否有效** ** b
$ b AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder()。find SHA1withRSA);
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder()。find(sigAlgId);

RSAPrivateCrtKeyParameters cps =(RSAPrivateCrtKeyParameters)keypair.getPrivate();
ContentSigner sigGen = new BcRSAContentSignerBuilder(sigAlgId,digAlgId).build(cps);

X509CertificateHolder certHolder = v3CertGen.build(sigGen);

CertificateFactory cf = CertificateFactory.getInstance(X.509);

//读取用户证书
InputStream is1 = new ByteArrayInputStream(certHolder.getEncoded());
X509Certificate eeCert =(X509Certificate)cf.generateCertificate(is1);
is1.close();


解决方案

在Rene Mayrhofer的帮助下,代码在这里提供:链接



X509CertificateGenerator。 java



我提供了我的实现,它只在本地测试环境中测试过,但它似乎可行:

  package net.jxta.impl.membership.pse; 

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Calendar;
import java.util.Date;
import java.util.logging.Logger;

import javax.security.auth.x500.X500Principal;

import org.apache.commons.codec.binary.Hex;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERBMPString;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DigestInfo;
import org.bouncycastle.asn1.x509.RSAPublicKeyStructure;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.TBSCertificateStructure;
import org.bouncycastle.asn1.x509.Time;
import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
import org.bouncycastle.asn1.x509.X509CertificateStructure;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
import org.bouncycastle.crypto.engines.RSAEngine;
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.jce.PrincipalUtil;
import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
import org.bouncycastle.jce.provider.X509CertificateObject;
import org.bouncycastle.x509.X509Util;
import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;

/ **此类使用Bouncycastle轻量级API以编程方式生成X.509证书。
*它假定一个CA证书和它的私钥是可用的,并且可以用
*这个CA签署新证书。该类的一些代码来自
* org.bouncycastle.x509.X509V3CertificateGenerator,但适用于使用轻量级API而不是
* JCE(这通常不适用于MIDP2.0) 。
*
* @author Rene Mayrhofer
* /
公共类X509CertificateGenerator {
/ **我们的log4j记录器。 * /
private static Logger logger = Logger.getLogger(X509CertificateGenerator.class.getName());

/ **这包含用于签署新证书的CA证书。该对象在构造函数中创建。 * /
私人X509Certificate caCert;
/ **这包含用于签署新证书的CA的私钥。该对象在构造函数中创建。 * /
private RSAPrivateCrtKeyParameters caPrivateKey;

私有布尔值useBCAPI;
private boolean useCACert;

public X509CertificateGenerator(String caFile,String caPassword,String caAlias,boolean useBCAPI)
throws KeyStoreException,NoSuchAlgorithmException,CertificateException,FileNotFoundException,IOException,UnrecoverableKeyException,InvalidKeyException,NoSuchProviderException,SignatureException {
this.useBCAPI = useBCAPI;
this.useCACert = true;
logger.info(从文件加载CA证书和私钥'+ caFile +',使用别名'+ caAlias +'加上
+(this.useBCAPI?)Bouncycastle轻量级API :JCE API));
KeyStore caKs = KeyStore.getInstance(PKCS12);
caKs.load(new FileInputStream(new File(caFile)),caPassword.toCharArray());

//从密钥存储区加载密钥条目
Key key = caKs.getKey(caAlias,caPassword.toCharArray());
if(key == null){
throw new RuntimeException(从keystore获得null key!);
}
RSAPrivateCrtKey privKey =(RSAPrivateCrtKey)key;
caPrivateKey = new RSAPrivateCrtKeyParameters(privKey.getModulus(),privKey.getPublicExponent(),privKey.getPrivateExponent(),
privKey.getPrimeP(),privKey.getPrimeQ(),privKey.getPrimeExponentP(),privKey .getPrimeExponentQ(),privKey.getCrtCoefficient());
//获取证书
caCert =(X509Certificate)caKs.getCertificate(caAlias);
if(caCert == null){
throw new RuntimeException(从keystore获得null证书!);

logger.info(成功加载CA密钥和证书,CA DN为'+ caCert.getSubjectDN()。getName()+');
caCert.verify(caCert.getPublicKey());
logger.info(使用自己的公钥验证了CA证书);

public X509CertificateGenerator(boolean useBCAPI)throws KeyStoreException,NoSuchAlgorithmException,CertificateException,FileNotFoundException,IOException,UnrecoverableKeyException,InvalidKeyException,NoSuchProviderException,SignatureException {
this.useBCAPI = useBCAPI;
this.useCACert = false;

$ b $ public X509Certificate createCertificate(String dn,int validityDays,X500Principal issuer,KeyPair keypairca)throws
IOException,InvalidKeyException,SecurityException,SignatureException,NoSuchAlgorithmException,DataLengthException,CryptoException,KeyStoreException, NoSuchProviderException,CertificateException,InvalidKeySpecException {
logger.info(生成区分主题名称的证书+
dn +',有效期为+ validityDays +天数);

RSAPrivateCrtKey pK =(RSAPrivateCrtKey)keypairca.getPrivate();
caPrivateKey = new RSAPrivateCrtKeyParameters(pK.getModulus(),pK.getPublicExponent(),pK.getPrivateExponent(),
pK.getPrimeP(),pK.getPrimeQ(),pK.getPrimeExponentP(),pK .getPrimeExponentQ(),pK.getCrtCoefficient());

SecureRandom sr = new SecureRandom();

PublicKey pubKey;
PrivateKey privKey;

logger.info(创建RSA密钥对);
//为新证书生成密钥对
if(useBCAPI){
RSAKeyPairGenerator gen = new RSAKeyPairGenerator();
gen.init(new RSAKeyGenerationParameters(BigInteger.valueOf(3),sr,1024,80));
AsymmetricCipherKeyPair keypair = gen.generateKeyPair();
logger.info(生成密钥对,提取组件并创建证书的公共结构);
RSAKeyParameters publicKey =(RSAKeyParameters)keypair.getPublic();
RSAPrivateCrtKeyParameters privateKey =(RSAPrivateCrtKeyParameters)keypair.getPrivate();
//用于获取证书的正确编码
RSAPublicKeyStructure pkStruct = new RSAPublicKeyStructure(publicKey.getModulus(),publicKey.getExponent());
logger.info(新公钥是'+新字符串(Hex.encodeHex(pkStruct.getEncoded()))+
,exponent =+ publicKey.getExponent()+,模数=+ publicKey.getModulus());
//证书所需的JCE格式 - 因为getEncoded()是必需的...
pubKey = KeyFactory.getInstance(RSA)。generatePublic(
new RSAPublicKeySpec(publicKey.getModulus ),publicKey.getExponent()));
//这个用于KeyStore
privKey = KeyFactory.getInstance(RSA)。generatePrivate(
new RSAPrivateCrtKeySpec(publicKey.getModulus(),publicKey.getExponent(),
privateKey.getExponent(),privateKey.getP(),privateKey.getQ(),
privateKey.getDP(),privateKey.getDQ(),privateKey.getQInv()));
}
else {
//这是密钥生成的JSSE方法
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(RSA);
keyGen.initialize(1024,sr);
KeyPair keypair = keyGen.generateKeyPair();
privKey = keypair.getPrivate();
pubKey = keypair.getPublic();
}

Calendar expiry = Calendar.getInstance();
expiry.add(Calendar.DAY_OF_YEAR,validityDays);

X509Name x509Name = new X509Name(CN =+ dn);
X509Name x509Issuer = new X509Name(issuer.getName());
V3TBSCertificateGenerator certGen = new V3TBSCertificateGenerator();
certGen.setSerialNumber(new DERInteger(BigInteger.valueOf(System.currentTimeMillis())));
certGen.setIssuer(x509Issuer); // issuer.getName()); // PrincipalUtil.getSubjectX509Principal(caCert));
certGen.setSubject(x509Name);
DERObjectIdentifier sigOID = X509Util.getAlgorithmOID(SHA1WithRSAEncryption);
AlgorithmIdentifier sigAlgId = new AlgorithmIdentifier(sigOID,new DERNull());
certGen.setSignature(sigAlgId);
certGen.setSubjectPublicKeyInfo(new SubjectPublicKeyInfo((ASN1Sequence)new ASN1InputStream(
new ByteArrayInputStream(pubKey.getEncoded()))。readObject()));
certGen.setStartDate(new Time(new Date(System.currentTimeMillis())));
certGen.setEndDate(new Time(expiry.getTime()));

logger.info(生成证书结构,创建SHA1摘要);
//注意:硬编码为SHA1 + RSA!
SHA1Digest消化器=新的SHA1Digest();
AsymmetricBlockCipher rsa = new PKCS1Encoding(new RSAEngine());
TBSCertificateStructure tbsCert = certGen.generateTBSCertificate();

ByteArrayOutputStream bOut = new ByteArrayOutputStream();
DEROutputStream dOut = new DEROutputStream(bOut);
dOut.writeObject(tbsCert);

//现在签署
byte []签名;
if(useBCAPI){
byte [] certBlock = bOut.toByteArray();
//首先创建摘要
logger.info(Block to sign is''+ new String(Hex.encodeHex(certBlock))+');
digester.update(certBlock,0,certBlock.length);
byte [] hash = new byte [digester.getDigestSize()];
digester.doFinal(hash,0);
//并签署
rsa.init(true,caPrivateKey);
DigestInfo dInfo = new DigestInfo(new AlgorithmIdentifier(X509ObjectIdentifiers.id_SHA1,null),hash);
byte [] digest = dInfo.getEncoded(ASN1Encodable.DER);
signature = rsa.processBlock(digest,0,digest.length);

else {
//或JCE方式
PrivateKey caPrivKey = KeyFactory.getInstance(RSA)。generatePrivate(
new RSAPrivateCrtKeySpec(caPrivateKey.getModulus( ),caPrivateKey.getPublicExponent(),
caPrivateKey.getExponent(),caPrivateKey.getP(),caPrivateKey.getQ(),
caPrivateKey.getDP(),caPrivateKey.getDQ(),caPrivateKey.getQInv )));

签名sig = Signature.getInstance(sigOID.getId());
sig.initSign(caPrivKey,sr);
sig.update(bOut.toByteArray());
signature = sig.sign();

logger.info(摘要的SHA1 / RSA签名是'+ new String(Hex.encodeHex(signature))+');

//最后构造证书结构
ASN1EncodableVector v = new ASN1EncodableVector();

v.add(tbsCert);
v.add(sigAlgId);
v.add(新的DERBitString(签名));

X509CertificateObject clientCert = new X509CertificateObject(new X509CertificateStructure(new DERSequence(v)));
logger.info(使用CA公钥验证证书的正确签名);

if(useCACert)clientCert.verify(caCert.getPublicKey());

返回clientCert;
}


}





I'm attempting to port JXTA to run on App Engine. Given that the BouncyCastle "BC" provider is not yet supported on App Engine, I have to port the existing JXTA code to generate a X509Certificate using white-listed classes. My knowledge of Crypto is minimal and i'm not certain that what i'm trying to accomplish is even possible. Here is the original code from PSEUtils.java from the JXTA project:

PSEUtils.java

There's a helper class which contains the java.security.cert.X509Certificate:

public static class IssuerInfo {
    public X509Certificate cert; // subject Cert
    public PrivateKey subjectPkey; // subject private key
    public X509Certificate issuer; // issuer Cert
    public PrivateKey issuerPkey; // issuer private key
}

In the method:

public static IssuerInfo genCert(X500Principal subject, KeyPair keypair, IssuerInfo issuerinfo)

I'm passing in subject as:

new X500Principal("CN="+useCN)

keypair as (from original code):

KeyPairGenerator g = KeyPairGenerator.getInstance("RSA");
g.initialize(1024, UTILS.srng);
KeyPair keypair = g.generateKeyPair();

and the jxta coded IssuerInfo.

Now since I can't pull in the bouncycastle.jce packages, I've had to remove the X509Principal and X509V3CertificateGenerator code that JXTA uses and attempt to replace it with an implementation that complies with the GAE restrictions. Here is what I currently have for the genCert method using org.bouncycastle.X509.X509v3CertificateBuilder.

SubjectPublicKeyInfo subPubKeyInfo =  SubjectPublicKeyInfo.getInstance(keypair.getPublic().getEncoded());

X509v3CertificateBuilder v3CertGen = new X509v3CertificateBuilder(
     new X500Name(issuer.getName()), 
     BigInteger.ONE, 
     today, until, 
     new X500Name(subject.getName()), 
     subPubKeyInfo);

The problem is that I can't get keypair.getPublic().getEncoded() to work with the SubjectPublicKeyInfo.getInstance() method. Throws java.lang.IllegalArgumentException: unknown object in factory: [B

Public Key appears to be populated upon inspection:

Sun RSA public key, 1024 bits
  modulus: 117521430893506212334140912845641570591161279468597426442875306202350445904550279678434051874985419676760802566018092318362676224355315431299979507080364677679613392086245588766565617009250512996843008784370448997729071786062596049780632058501646041736216482596596901215941577208285499619376322050871534546271
  public exponent: 65537

I've found the following SO link which demonstrates this code working:

Sign CSR using Bouncy Castle

My attempt to convert genCert is below but for some reason, I can't get past creating SubjectPublicKeyInfo from the encoded public key?

Any help is greatly appreciated.

public static IssuerInfo genCert(X500Principal subject, KeyPair keypair, IssuerInfo issuerinfo)  {
    IssuerInfo info = new IssuerInfo();
    try {
        // set up issuer
        PrivateKey signer;
        X500Principal issuer;

        if (null == issuerinfo) { // self-signed root cert
            signer = keypair.getPrivate();
            issuer = new X500Principal(subject.getEncoded());
        } else { // issuer signed service sert
            signer = issuerinfo.subjectPkey;
            X500Principal issuer_subject = issuerinfo.cert.getSubjectX500Principal();
            issuer = new X500Principal(issuer_subject.getEncoded());
        }

        // set validity 10 years from today
        Date today = new Date();
        Calendar cal = Calendar.getInstance();

        cal.setTime(today);
        cal.add(Calendar.YEAR, 10);
        Date until = cal.getTime();

        SubjectPublicKeyInfo subPubKeyInfo =  SubjectPublicKeyInfo.getInstance(keypair.getPublic().getEncoded());

    //**Can't get here so i'm not sure if the rest of this works?**

        AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
        AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);

        RSAPrivateCrtKeyParameters cps = (RSAPrivateCrtKeyParameters) keypair.getPrivate();
        ContentSigner sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(cps);            

     X509CertificateHolder certHolder = v3CertGen.build(sigGen);

     CertificateFactory cf = CertificateFactory.getInstance("X.509");

     // Read user Certificate
     InputStream is1 = new ByteArrayInputStream(certHolder.getEncoded());
     X509Certificate eeCert = (X509Certificate) cf.generateCertificate(is1);
     is1.close();

解决方案

I was able to accomplish this with help from Rene Mayrhofer's code provided here: link.

X509CertificateGenerator.java

I've provided my implementation which has only be tested in a local testing environment but it appears to work:

package net.jxta.impl.membership.pse;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Calendar;
import java.util.Date;
import java.util.logging.Logger;

import javax.security.auth.x500.X500Principal;

import org.apache.commons.codec.binary.Hex;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERBMPString;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DigestInfo;
import org.bouncycastle.asn1.x509.RSAPublicKeyStructure;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.TBSCertificateStructure;
import org.bouncycastle.asn1.x509.Time;
import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
import org.bouncycastle.asn1.x509.X509CertificateStructure;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
import org.bouncycastle.crypto.engines.RSAEngine;
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.jce.PrincipalUtil;
import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
import org.bouncycastle.jce.provider.X509CertificateObject;
import org.bouncycastle.x509.X509Util;
import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;

/** This class uses the Bouncycastle lightweight API to generate X.509 certificates programmatically.
 * It assumes a CA certificate and its private key to be available and can sign the new certificate with
 * this CA. Some of the code for this class was taken from 
 * org.bouncycastle.x509.X509V3CertificateGenerator, but adapted to work with the lightweight API instead of
 * JCE (which is usually not available on MIDP2.0). 
 * 
 * @author Rene Mayrhofer
 */
public class X509CertificateGenerator {
    /** Our log4j logger. */
    private static Logger logger = Logger.getLogger(X509CertificateGenerator.class.getName());

    /** This holds the certificate of the CA used to sign the new certificate. The object is created in the constructor. */
    private X509Certificate caCert;
    /** This holds the private key of the CA used to sign the new certificate. The object is created in the constructor. */
    private RSAPrivateCrtKeyParameters caPrivateKey;

    private boolean useBCAPI;
    private boolean useCACert;

    public X509CertificateGenerator(String caFile, String caPassword, String caAlias, boolean useBCAPI) 
            throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, UnrecoverableKeyException, InvalidKeyException, NoSuchProviderException, SignatureException {
        this.useBCAPI = useBCAPI;
        this.useCACert = true;
        logger.info("Loading CA certificate and private key from file '" + caFile + "', using alias '" + caAlias + "' with "
                + (this.useBCAPI ? "Bouncycastle lightweight API" : "JCE API"));
        KeyStore caKs = KeyStore.getInstance("PKCS12");
        caKs.load(new FileInputStream(new File(caFile)), caPassword.toCharArray());

        // load the key entry from the keystore
        Key key = caKs.getKey(caAlias, caPassword.toCharArray());
        if (key == null) {
            throw new RuntimeException("Got null key from keystore!"); 
        }
        RSAPrivateCrtKey privKey = (RSAPrivateCrtKey) key;
        caPrivateKey = new RSAPrivateCrtKeyParameters(privKey.getModulus(), privKey.getPublicExponent(), privKey.getPrivateExponent(),
                privKey.getPrimeP(), privKey.getPrimeQ(), privKey.getPrimeExponentP(), privKey.getPrimeExponentQ(), privKey.getCrtCoefficient());
        // and get the certificate
        caCert = (X509Certificate) caKs.getCertificate(caAlias);
        if (caCert == null) {
            throw new RuntimeException("Got null cert from keystore!"); 
        }
        logger.info("Successfully loaded CA key and certificate. CA DN is '" + caCert.getSubjectDN().getName() + "'");
        caCert.verify(caCert.getPublicKey());
        logger.info("Successfully verified CA certificate with its own public key.");
    }
    public X509CertificateGenerator(boolean useBCAPI) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, UnrecoverableKeyException, InvalidKeyException, NoSuchProviderException, SignatureException {
        this.useBCAPI = useBCAPI;
        this.useCACert = false;
    }

    public X509Certificate createCertificate(String dn, int validityDays, X500Principal issuer, KeyPair keypairca) throws 
            IOException, InvalidKeyException, SecurityException, SignatureException, NoSuchAlgorithmException, DataLengthException, CryptoException, KeyStoreException, NoSuchProviderException, CertificateException, InvalidKeySpecException {
        logger.info("Generating certificate for distinguished subject name '" + 
                dn + "', valid for " + validityDays + " days");

        RSAPrivateCrtKey pK = (RSAPrivateCrtKey) keypairca.getPrivate();
        caPrivateKey = new RSAPrivateCrtKeyParameters(pK.getModulus(), pK.getPublicExponent(), pK.getPrivateExponent(),
                pK.getPrimeP(), pK.getPrimeQ(), pK.getPrimeExponentP(), pK.getPrimeExponentQ(), pK.getCrtCoefficient());

        SecureRandom sr = new SecureRandom();

        PublicKey pubKey;
        PrivateKey privKey;

        logger.info("Creating RSA keypair");
        // generate the keypair for the new certificate
        if (useBCAPI) {
            RSAKeyPairGenerator gen = new RSAKeyPairGenerator();
            gen.init(new RSAKeyGenerationParameters(BigInteger.valueOf(3), sr, 1024, 80));
            AsymmetricCipherKeyPair keypair = gen.generateKeyPair();
            logger.info("Generated keypair, extracting components and creating public structure for certificate");
            RSAKeyParameters publicKey = (RSAKeyParameters) keypair.getPublic();
            RSAPrivateCrtKeyParameters privateKey = (RSAPrivateCrtKeyParameters) keypair.getPrivate();
            // used to get proper encoding for the certificate
            RSAPublicKeyStructure pkStruct = new RSAPublicKeyStructure(publicKey.getModulus(), publicKey.getExponent());
            logger.info("New public key is '" + new String(Hex.encodeHex(pkStruct.getEncoded())) + 
                    ", exponent=" + publicKey.getExponent() + ", modulus=" + publicKey.getModulus());
            // JCE format needed for the certificate - because getEncoded() is necessary...
            pubKey = KeyFactory.getInstance("RSA").generatePublic(
                    new RSAPublicKeySpec(publicKey.getModulus(), publicKey.getExponent()));
            // and this one for the KeyStore
            privKey = KeyFactory.getInstance("RSA").generatePrivate(
                    new RSAPrivateCrtKeySpec(publicKey.getModulus(), publicKey.getExponent(),
                            privateKey.getExponent(), privateKey.getP(), privateKey.getQ(), 
                            privateKey.getDP(), privateKey.getDQ(), privateKey.getQInv()));
        }
        else {
            // this is the JSSE way of key generation
            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
            keyGen.initialize(1024, sr);
            KeyPair keypair = keyGen.generateKeyPair();
            privKey = keypair.getPrivate();
            pubKey = keypair.getPublic();
        }

        Calendar expiry = Calendar.getInstance();
        expiry.add(Calendar.DAY_OF_YEAR, validityDays);

        X509Name x509Name = new X509Name("CN=" + dn);
        X509Name x509Issuer = new X509Name(issuer.getName());
        V3TBSCertificateGenerator certGen = new V3TBSCertificateGenerator();
        certGen.setSerialNumber(new DERInteger(BigInteger.valueOf(System.currentTimeMillis())));
        certGen.setIssuer(x509Issuer);//issuer.getName());//PrincipalUtil.getSubjectX509Principal(caCert));
        certGen.setSubject(x509Name);
        DERObjectIdentifier sigOID = X509Util.getAlgorithmOID("SHA1WithRSAEncryption");
        AlgorithmIdentifier sigAlgId = new AlgorithmIdentifier(sigOID, new DERNull());
        certGen.setSignature(sigAlgId);
        certGen.setSubjectPublicKeyInfo(new SubjectPublicKeyInfo((ASN1Sequence)new ASN1InputStream(
                new ByteArrayInputStream(pubKey.getEncoded())).readObject()));
        certGen.setStartDate(new Time(new Date(System.currentTimeMillis())));
        certGen.setEndDate(new Time(expiry.getTime()));

        logger.info("Certificate structure generated, creating SHA1 digest");
        // attention: hard coded to be SHA1+RSA!
        SHA1Digest digester = new SHA1Digest();
        AsymmetricBlockCipher rsa = new PKCS1Encoding(new RSAEngine());
        TBSCertificateStructure tbsCert = certGen.generateTBSCertificate();

        ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
        DEROutputStream         dOut = new DEROutputStream(bOut);
        dOut.writeObject(tbsCert);

        // and now sign
        byte[] signature;
        if (useBCAPI) {
            byte[] certBlock = bOut.toByteArray();
            // first create digest
            logger.info("Block to sign is '" + new String(Hex.encodeHex(certBlock)) + "'");     
            digester.update(certBlock, 0, certBlock.length);
            byte[] hash = new byte[digester.getDigestSize()];
            digester.doFinal(hash, 0);
            // and sign that
            rsa.init(true, caPrivateKey);
            DigestInfo dInfo = new DigestInfo( new AlgorithmIdentifier(X509ObjectIdentifiers.id_SHA1, null), hash);
            byte[] digest = dInfo.getEncoded(ASN1Encodable.DER);
            signature = rsa.processBlock(digest, 0, digest.length);
        }
        else {
            // or the JCE way
            PrivateKey caPrivKey = KeyFactory.getInstance("RSA").generatePrivate(
                    new RSAPrivateCrtKeySpec(caPrivateKey.getModulus(), caPrivateKey.getPublicExponent(),
                            caPrivateKey.getExponent(), caPrivateKey.getP(), caPrivateKey.getQ(), 
                            caPrivateKey.getDP(), caPrivateKey.getDQ(), caPrivateKey.getQInv()));

            Signature sig = Signature.getInstance(sigOID.getId());
            sig.initSign(caPrivKey, sr);
            sig.update(bOut.toByteArray());
            signature = sig.sign();
        }
        logger.info("SHA1/RSA signature of digest is '" + new String(Hex.encodeHex(signature)) + "'");

        // and finally construct the certificate structure
        ASN1EncodableVector  v = new ASN1EncodableVector();

        v.add(tbsCert);
        v.add(sigAlgId);
        v.add(new DERBitString(signature));

        X509CertificateObject clientCert = new X509CertificateObject(new X509CertificateStructure(new DERSequence(v))); 
        logger.info("Verifying certificate for correct signature with CA public key");

        if(useCACert) clientCert.verify(caCert.getPublicKey());

        return clientCert;
    }


}


这篇关于使用bouncycastle X509v3CertificateBuilder生成X509Certificate的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆