在我生成的证书中添加新的扩展名 [英] Adding a new Extension to my generated certificate

查看:232
本文介绍了在我生成的证书中添加新的扩展名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要在证书中添加新的OID扩展 1.3.6.1.5.5.7.1.26 。我在证书中得到了此OID扩展名,但出现以下错误:

I need to add a new Extension of OID 1.3.6.1.5.5.7.1.26 in my certificate. I got this OID extension in my certificate but with the following error:


证书扩展名:10
[1]:ObjectId: 1.3.6.1.5.5.7.1.26 Criticality = false

扩展名未知:DER编码的OCTET字符串=

0000:04 0C 30 0A 13 08 33 39 20 64 63 20 32 62 ..0 ...

39 dc 2b

Certificate Extensions: 10 [1]: ObjectId: 1.3.6.1.5.5.7.1.26 Criticality=false
Extension unknown: DER encoded OCTET string =
0000: 04 0C 30 0A 13 08 33 39 20 64 63 20 32 62 ..0...
39 dc 2b

我想要这个OID可以像 AuthorityInfoAccess 等其他扩展名一样被识别。

I want this OID to be recognized similar to other extensions like AuthorityInfoAccess, etc.

我需要编辑 Bouncy Castle的罐子吗? > X509类?

Do I need to edit the jar of Bouncy Castle X509 class?

我使用ACME4j作为客户端,并使用Letsencrypt Boulder作为我的服务器。

Im using ACME4j as a client and Letsencrypt Boulder as my server.

以下是CSR Builder代码

Here is the CSR Builder code for signing up the certificate.

public void sign(KeyPair keypair) throws IOException {
    //Security.addProvider(new BouncyCastleProvider());
    Objects.requireNonNull(keypair, "keypair");
    if (namelist.isEmpty()) {
        throw new IllegalStateException("No domain was set");
    }

    try {
        GeneralName[] gns = new GeneralName[namelist.size()];
        for (int ix = 0; ix < namelist.size(); ix++) {
            gns[ix] = new GeneralName(GeneralName.dNSName,namelist.get(ix));
        }
        SignatureAlgorithmIdentifierFinder algFinder = new 
                DefaultSignatureAlgorithmIdentifierFinder();
        GeneralNames subjectAltName = new GeneralNames(gns);


        PKCS10CertificationRequestBuilder p10Builder = new     JcaPKCS10CertificationRequestBuilder(namebuilder.build(), keypair.getPublic());

        ExtensionsGenerator extensionsGenerator = new ExtensionsGenerator();
        extensionsGenerator.addExtension(Extension.subjectAlternativeName,     false, subjectAltName);
        //extensionsGenerator.addExtension(Extension.authorityInfoAccess,         true, subjectAltName);
        //extensionsGenerator.addExtension(new ASN1ObjectIdentifier("TBD"),     false, subjectAltName);
        //extensionsGenerator.addExtension(new     ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.24"), false, subjectAltName);
        extensionsGenerator.addExtension(new     ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.26").intern(), false, subjectAltName);
        //extentionsGenerator.addExtension();
            p10Builder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest,     extensionsGenerator.generate());


        PrivateKey pk = keypair.getPrivate();
        /*JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder(
                        pk instanceof ECKey ? EC_SIGNATURE_ALG :     EC_SIGNATURE_ALG);
        ContentSigner signer = csBuilder.build(pk);*/

        if(pk instanceof ECKey)
        {
            AlgorithmIdentifier sigAlg = algFinder.find("SHA1withECDSA");
              AlgorithmIdentifier digAlg = new     DefaultDigestAlgorithmIdentifierFinder().
                    find(sigAlg);
            ContentSigner signer = new     JcaContentSignerBuilder("SHA256with"+pk.getAlgorithm()).setProvider(BOUNCY_CASTL    E_PROVIDER).build(keypair.getPrivate());

            csr=p10Builder.build(signer);
            System.out.println("ZIPED CSR ECDSA: "+csr);
        }
        else
        {
            ContentSigner signer = new     JcaContentSignerBuilder("SHA256with"+pk.getAlgorithm()).build(keypair.getPrivate    ()); 
            csr = p10Builder.build(signer);
            System.out.println("ZIPED CSR RSA: "+csr);
        }

        //csr = p10Builder.build(signer);
    } catch (Exception ex) {
        ex.printStackTrace();;
    }
}


推荐答案

注意:对于这些代码,我使用了 bcprov-jdk15on 1.56

Note: for these codes I used bcprov-jdk15on 1.56

关于您的代码的一些注释。首先,请注意ASN1结构:

Some comments about your code. First of all, note the ASN1 structure:

TNAuthorizationList ::= SEQUENCE SIZE (1..MAX) OF TNEntry

TNEntry ::= CHOICE {
  spc   [0] ServiceProviderCodeList,
  range [1] TelephoneNumberRange,
  one       E164Number
}

请注意, TNEntry 是一个选择 TNAuthorizationList TNEntry 对象的序列 。因此,您的类名称应更改为 TNEntry 。在下面的代码中,请记住,我已将类名称更改为 TNEntry

Note that TNEntry is a choice, and TNAuthorizationList is a sequence of TNEntry objects. So your class name should be changed to TNEntry. In the code below, please remember that I've changed the class name to TNEntry.

我也在课堂上做了一些改动。在 getInstance(Object obj)方法中, spc range 字段的类型不正确(根据ASN1的定义,它们都是序列):

I've also changed some things in this class. In getInstance(Object obj) method, the types of spc and range fields are incorrect (according to ASN1 definition, they are both sequences):

switch (tag) {
    case spc:
    case range: // both are sequences
        return new TNEntry(tag, ASN1Sequence.getInstance(tagObj, false));
    // not sure about "one" field, as it's not tagged
}

我只是不知道如何处理一个字段,因为该字段未标记。也许应该是 DERIA5String ,或者还有另一种类型的未标记选择。

I just don't know how to handle the one field, as it's not tagged. Maybe it should be a DERIA5String, or maybe there's another type for "untagged" choices.

在同一类中(记住,我已将其名称更改为 TNEntry ),我还删除了构造函数 public TNEntry(int tag,String name),因为我不确定它是否适用(至少我不需要使用它,但您可以根据需要保留它),并且我已将 toString 返回更易读的字符串的方法:

In this same class (remember, I've changed its name to TNEntry), I also removed the constructor public TNEntry(int tag, String name) because I'm not sure if it applies (at least I didn't need to use it, but you can keep it if you want), and I've changed toString method to return a more readable string:

public String toString() {
    String sep = System.getProperty("line.separator");
    StringBuffer buf = new StringBuffer();

    buf.append(this.getClass().getSimpleName());
    buf.append(" [").append(tag);
    buf.append("]: ");
    switch (tag) {
        case spc:
            buf.append("ServiceProviderCodeList: ").append(sep);
            ASN1Sequence seq = (ASN1Sequence) this.obj;
            int size = seq.size();
            for (int i = 0; i < size; i++) {
                // all elements are DERIA5Strings
                DERIA5String str = (DERIA5String) seq.getObjectAt(i);
                buf.append("    ");
                buf.append(str.getString());
                buf.append(sep);
            }
            break;

        case range:
            buf.append("TelephoneNumberRange: ").append(sep);

            // there are always 2 elements in TelephoneNumberRange
            ASN1Sequence s = (ASN1Sequence) this.obj;
            DERIA5String str = (DERIA5String) s.getObjectAt(0);
            buf.append("    start: ");
            buf.append(str.getString());
            buf.append(sep);
            ASN1Integer count = (ASN1Integer) s.getObjectAt(1);
            buf.append("    count: ");
            buf.append(count.toString());
            buf.append(sep);
            break;

        default:
            buf.append(obj.toString());
    }

    return buf.toString();
}

我还创建了 TNAuthorizationList 类,其中包含 TNEntry 对象的序列(请记住,我已将您的类名称更改为 TNEntry ,因此此 TNAuthorizationList 类是一个不同的类)。请注意,我还创建了一个常量来保存 OID (只是为了使事情变得简单一些):

And I also created a TNAuthorizationList class, which holds the sequence of TNEntry objects (remember that I've changed your class name to TNEntry, so this TNAuthorizationList class is a different one). Note that I also created a constant to hold the OID (just to make things a little bit easier):

public class TNAuthorizationList extends ASN1Object {
    // put OID in a constant, so I don't have to remember it all the time
    public static final ASN1ObjectIdentifier TN_AUTH_LIST_OID = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.1.26");

    private TNEntry[] entries;

    public TNAuthorizationList(TNEntry[] entries) {
        this.entries = entries;
    }

    public static TNAuthorizationList getInstance(Object obj) {
        if (obj instanceof TNAuthorizationList) {
            return (TNAuthorizationList) obj;
        }
        if (obj != null) {
            return new TNAuthorizationList(ASN1Sequence.getInstance(obj));
        }

        return null;
    }

    public static TNAuthorizationList getInstance(ASN1TaggedObject obj, boolean explicit) {
        return getInstance(ASN1Sequence.getInstance(obj, explicit));
    }

    private TNAuthorizationList(ASN1Sequence seq) {
        this.entries = new TNEntry[seq.size()];

        for (int i = 0; i != seq.size(); i++) {
            entries[i] = TNEntry.getInstance(seq.getObjectAt(i));
        }
    }

    public TNEntry[] getEntries() {
        TNEntry[] tmp = new TNEntry[entries.length];
        System.arraycopy(entries, 0, tmp, 0, entries.length);
        return tmp;
    }

    @Override
    public ASN1Primitive toASN1Primitive() {
        return new DERSequence(entries);
    }

    public String toString() {
        String sep = System.getProperty("line.separator");
        StringBuffer buf = new StringBuffer();

        buf.append(this.getClass().getSimpleName());
        buf.append(":").append(sep);
        for (TNEntry tnEntry : entries) {
            buf.append("  ");
            buf.append(tnEntry.toString());
            buf.append(sep);
        }
        return buf.toString();
    }
}

现在,将此扩展名添加到证书中,我已经完成了这段代码(使用了一些示例数据,因为我不知道在现实情况下每个字段应该包含什么):

Now, to add this extension to a certificate, I've done this code (with some sample data as I don't know what should be in each field in a real world situation):

X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(etc...);

// create TNEntries for TNAuthorizationList
TNEntry[] entries = new TNEntry[2];

// create a "spc" entry
DERIA5String[] cList = new DERIA5String[] { new DERIA5String("spc1"), new DERIA5String("spc2") };
DERSequence spc = new DERSequence(cList);
entries[0] = TNEntry.getInstance(new DERTaggedObject(false, TNEntry.spc, spc));

// create a "range" entry
DERSequence range = new DERSequence(new ASN1Encodable[] { new DERIA5String("123456"), new ASN1Integer(1) });
entries[1] = TNEntry.getInstance(new DERTaggedObject(false, TNEntry.range, range));

TNAuthorizationList tnAuthList = new TNAuthorizationList(entries);
builder.addExtension(TNAuthorizationList.TN_AUTH_LIST_OID, false, tnAuthList);

拥有证书对象( X509Certificate 在我的示例中),您可以这样做:

Once you have the certificate object (a X509Certificate in my example), you can do:

// cert is a X509Certificate instance
ASN1Primitive value = X509ExtensionUtil.fromExtensionValue(cert.getExtensionValue(TNAuthorizationList.TN_AUTH_LIST_OID.getId()));
TNAuthorizationList authList = TNAuthorizationList.getInstance(value);
System.out.println(authList.toString());

输出将是:

TNAuthorizationList:
  TNEntry [0]: ServiceProviderCodeList: 
    spc1
    spc2

  TNEntry [1]: TelephoneNumberRange: 
    start: 123456
    count: 1






注意:


  • 正如我所说,此代码是不完整的,因为我不知道如何处理 TNEntry one 字段,因为它没有被标记(我不知道它是否必须是 DERIA5String 或未标记字段是否存在另一种对象)。

  • 您还可以做一些改进:


    • ServiceProviderCodeList 可以包含1到3个元素,因此您可以验证其大小

    • TelephoneNumberRange 开始字段具有特定格式( FROM( 0123456789#*)我认为这意味着只接受这些字符),因此您也可以对其进行验证

    • ServiceProviderCodeList 和<$ c $创建值c> TelephoneNumberRange ,我已经手动创建了 DERSequence 对象,但是您可以根据需要为它们创建自定义类: ServiceProviderCodeList 可以保存 DERIA5String 的列表,并在其构造函数中执行适当的验证(大小从1到3),而 TelephoneNumberRange 可以具有 start count 字段(对 start 值进行正确验证)-和 toASN1Primitive 只需要按正确的顺序返回其字段的 DERSequence

    • As I said, this code is incomplete because I'm not sure how to handle the one field of TNEntry, because it's not tagged (I don't know if it must be a DERIA5String or if there's another type of object for an "untagged" field).
    • You could also do some improvements:
      • ServiceProviderCodeList can have 1 to 3 elements, so you could validate its size
      • TelephoneNumberRange: the start field has a specific format (FROM ("0123456789#*") which I think it means only these characters are accepted), so you could also validate it
      • To create the values for ServiceProviderCodeList and TelephoneNumberRange, I've created the DERSequence objects by hand, but you can create custom classes for them if you want: ServiceProviderCodeList could hold a list of DERIA5String and perform proper validations in its constructor (size from 1 to 3), and TelephoneNumberRange could have start and count fields (with proper validation of start value) - and toASN1Primitive just need to return a DERSequence of its fields in the right order

      为您的解析问题,我已经检查了 acme4j代码,它使用 java.security.cert.X509Certificate 类。此类的 toString()方法(使用Sun的默认提供程序时)正在生成此扩展未知输出(根据相应的代码)。

      For your parsing issues, I've checked acme4j code and it uses a java.security.cert.X509Certificate class. The toString() method of this class (when using Sun's default provider) is generating this "extension unknown" output (according to the corresponding code).

      因此,为了正确解析它(显示如上所述的格式化输出),您可能必须更改acme4j的代码(或编写自己的代码),一个新的 toString()方法,并在此方法中包括新的 TNAuthorizationList 类。

      So, in order to parse it correctly (show the formatted output as described above), you'll probably have to change acme4j's code (or write your own), creating a new toString() method and include the new TNAuthorizationList classes in this method.

      当您提供显示如何使用acme4j的代码时,如果需要,我将相应地更新此答案。

      When you provide the code showing how you're using acme4j, I'll update this answer accordingly, if needed.

      这篇关于在我生成的证书中添加新的扩展名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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