使用Bouncy Castle在Java中创建自定义X509 v3扩展

使用Bouncy Castle在Java中创建自定义X509 v3扩展,java,openssl,certificate,x509certificate,bouncycastle,Java,Openssl,Certificate,X509certificate,Bouncycastle,我已经成功地使用Bouncy Castle的X509v3CertificateBuilder Java类创建了带有标准V3扩展的X509证书。我现在正在尝试创建带有自定义扩展名的证书 我可以使用addExtension(…)方法创建自定义扩展,但是,证书中的结果值不是我想要的。例如,我希望在自定义OID 1.2.3.4“00 00 00 FF FF FF”下的证书中列出这些精确的八位字节。我尝试过的每件事都用ASN1编码将八位字节字符串包装起来,结果是“04 08 00 00 00 FF FF

我已经成功地使用Bouncy Castle的X509v3CertificateBuilder Java类创建了带有标准V3扩展的X509证书。我现在正在尝试创建带有自定义扩展名的证书

我可以使用addExtension(…)方法创建自定义扩展,但是,证书中的结果值不是我想要的。例如,我希望在自定义OID 1.2.3.4“00 00 00 FF FF FF”下的证书中列出这些精确的八位字节。我尝试过的每件事都用ASN1编码将八位字节字符串包装起来,结果是“04 08 00 00 00 FF FF FF”

基本上,我想用Java创建一个具有自定义扩展名的证书,该扩展名与使用具有以下配置的扩展名文件使用OpenSSL创建证书时的外观相同:

1.2.3.4=DER:00:00:00:00:FF:FF:FF:FF
X509v3CertificateBuilder类是否可以以干净的方式实现这一点

下面是创建“不正确”值的代码片段


证书是ASN.1编码的,所以扩展值也应该是ASN.1编码的。04为八位字节字符串类型,08为该八位字节字符串的长度。BouncyCastle对扩展数据的格式一无所知,很可能这就是它不剥离标记和长度的原因,您应该手动解码该数据。

在尝试了许多不同的选项后,我认为不可能使用X509v3CertificateBuilder创建具有原始(非ASN.1编码)值的扩展。addExtension()方法期望或更改输入值为ASN.1编码

然而,在查看了X509v3CertificateBuilder在幕后使用的方法的BouncyCastle源代码之后,我找到了一种使用其他类的方法。涉及的代码行更多,但它相当简单,并给出了所需的结果

下面是允许使用原始值进行自定义扩展的代码

  // Raw value to place in cert for OID 1.2.3.4.
  byte[] bytearray = {0, 0, 0, 0, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};

  // Start creating the certificate beginning with the TBS certificate.      
  V3TBSCertificateGenerator tbsGen = new V3TBSCertificateGenerator();
  tbsGen.setSerialNumber(new ASN1Integer(serialNum));
  tbsGen.setIssuer(issuer);
  tbsGen.setStartDate(new Time(new Date(startDate)));
  tbsGen.setEndDate(new Time(new Date(endDate)));
  tbsGen.setSubject(new X500Name(dn));
  tbsGen.setSubjectPublicKeyInfo(SubjectPublicKeyInfo.getInstance(certPubKey.getEncoded()));
  tbsGen.setSignature(sigGen.getAlgorithmIdentifier());

  // The Key Usage extension:
  X509KeyUsage keyuse = new X509KeyUsage(
     X509KeyUsage.digitalSignature |
     X509KeyUsage.nonRepudiation   |
     X509KeyUsage.keyEncipherment  |
     X509KeyUsage.dataEncipherment);    
  Extension keyUsageExt =
     new Extension(
        Extension.keyUsage,
        true,
        keyuse.getEncoded());

  // The Basic Constraints extension:
  BasicConstraints basic = new BasicConstraints(false);
  Extension basicExt =
     new Extension(
        Extension.basicConstraints,
        false,
        basic.getEncoded());

  // The Custom extension:    
  ASN1ObjectIdentifier asn1iod =
     new ASN1ObjectIdentifier("1.2.3.4");      
  Extension customExt =
     new Extension(
        asn1iod,
        false, 
        bytearray);

  Extension[] extArray = {keyUsageExt, basicExt, customExt};
  tbsGen.setExtensions(new Extensions(extArray));

  // Create the TBS certificate.
  TBSCertificate tbsCert = tbsGen.generateTBSCertificate();

  // Sign the certificate.
  OutputStream ostream       = sigGen.getOutputStream();
  DEROutputStream derOstream = new DEROutputStream(ostream);
  derOstream.writeObject(tbsCert);
  ostream.close();      
  byte[] tbsSig = sigGen.getSignature();

  // Assemble the full X509 certificate. (TBS + Sig Alg + Sig)
  ASN1EncodableVector asnVector = new ASN1EncodableVector();
  asnVector.add(tbsCert);
  asnVector.add(sigGen.getAlgorithmIdentifier());
  asnVector.add(new DERBitString(tbsSig));
  X509CertificateHolder certHolder =
     new X509CertificateHolder(
        org.bouncycastle.asn1.x509.Certificate.getInstance(new DERSequence(asnVector)));

  X509Certificate cert =
     new JcaX509CertificateConverter()
        .setProvider(BC).getCertificate(certHolder);

这些额外的字节(0408)是ASN.1编码的标记类型和标记长度。看起来您的BouncyCastle在需要时(在调用ext.getExtnValue()时)没有剥离它们,或者您使用的BouncyCastle错误。鉴于它是开源的,“源代码是最好的文档”,您可以查看源代码以获得提示。谢谢Eugene!我可以查看源代码并使用其他方法来完成所需的操作。谢谢Nickolay,但是使用此证书的应用程序要求值为原始值,而不是ASN.1编码。我能够在Bouncy Castle中找到其他方法来创建原始值。
  // Raw value to place in cert for OID 1.2.3.4.
  byte[] bytearray = {0, 0, 0, 0, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};

  // Start creating the certificate beginning with the TBS certificate.      
  V3TBSCertificateGenerator tbsGen = new V3TBSCertificateGenerator();
  tbsGen.setSerialNumber(new ASN1Integer(serialNum));
  tbsGen.setIssuer(issuer);
  tbsGen.setStartDate(new Time(new Date(startDate)));
  tbsGen.setEndDate(new Time(new Date(endDate)));
  tbsGen.setSubject(new X500Name(dn));
  tbsGen.setSubjectPublicKeyInfo(SubjectPublicKeyInfo.getInstance(certPubKey.getEncoded()));
  tbsGen.setSignature(sigGen.getAlgorithmIdentifier());

  // The Key Usage extension:
  X509KeyUsage keyuse = new X509KeyUsage(
     X509KeyUsage.digitalSignature |
     X509KeyUsage.nonRepudiation   |
     X509KeyUsage.keyEncipherment  |
     X509KeyUsage.dataEncipherment);    
  Extension keyUsageExt =
     new Extension(
        Extension.keyUsage,
        true,
        keyuse.getEncoded());

  // The Basic Constraints extension:
  BasicConstraints basic = new BasicConstraints(false);
  Extension basicExt =
     new Extension(
        Extension.basicConstraints,
        false,
        basic.getEncoded());

  // The Custom extension:    
  ASN1ObjectIdentifier asn1iod =
     new ASN1ObjectIdentifier("1.2.3.4");      
  Extension customExt =
     new Extension(
        asn1iod,
        false, 
        bytearray);

  Extension[] extArray = {keyUsageExt, basicExt, customExt};
  tbsGen.setExtensions(new Extensions(extArray));

  // Create the TBS certificate.
  TBSCertificate tbsCert = tbsGen.generateTBSCertificate();

  // Sign the certificate.
  OutputStream ostream       = sigGen.getOutputStream();
  DEROutputStream derOstream = new DEROutputStream(ostream);
  derOstream.writeObject(tbsCert);
  ostream.close();      
  byte[] tbsSig = sigGen.getSignature();

  // Assemble the full X509 certificate. (TBS + Sig Alg + Sig)
  ASN1EncodableVector asnVector = new ASN1EncodableVector();
  asnVector.add(tbsCert);
  asnVector.add(sigGen.getAlgorithmIdentifier());
  asnVector.add(new DERBitString(tbsSig));
  X509CertificateHolder certHolder =
     new X509CertificateHolder(
        org.bouncycastle.asn1.x509.Certificate.getInstance(new DERSequence(asnVector)));

  X509Certificate cert =
     new JcaX509CertificateConverter()
        .setProvider(BC).getCertificate(certHolder);