【问题标题】:Adding a new Extension to my generated certificate向我生成的证书添加新扩展
【发布时间】:2019-03-02 23:41:00
【问题描述】:

我需要在我的证书中添加一个新的 OID 扩展 1.3.6.1.5.5.7.1.26。我的证书中有此 OID 扩展,但出现以下错误:

证书扩展: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 直流 2b

我希望这个 OID 能够像 AuthorityInfoAccess 等其他扩展一样被识别。

我需要编辑 Bouncy Castle X509 类的 jar 吗?

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

这是用于注册证书的 CSR Builder 代码。

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();;
    }
}

【问题讨论】:

  • 请包含您用于生成和验证证书的代码以及错误的完整堆栈跟踪。
  • 请同时添加您用于解析证书的工具(其中发生错误)。是 openssl 还是带有/不带 bouncycastle 的普通 java?
  • 我已经搜索过这个 OID 但找不到它:oid-info.com/get/1.3.6.1.5.5.7.1.26(也许这就是它不为人知的原因)。它应该是什么,针对特定上下文的自定义扩展?在上面的代码中,您向其中添加了 subjectAltName,但 Subject Alternative Name 已经有自己的 OID,不应将其添加到 1.3.6.1.5.5.7.1.26。 1.3.6.1.5.5.7.1.26 应该是什么值?
  • 您可以在此链接datatracker.ietf.org/doc/draft-ietf-stir-certificates/… 中找到有关此 OID 的详细信息,您可以在那里找到语法。我刚刚添加了主题备用名称,但它应该具有要传递的 SPID 值之类的东西。但为此,我需要一个具有正确 ASN1 编码值的 TN 授权列表的类!
  • 错误信息“未知扩展”来自哪个应用程序?

标签: ssl-certificate x509certificate bouncycastle csr java-security


【解决方案1】:

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

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

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

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

请注意,TNEntry 是一个选择TNAuthorizationListTNEntry 对象的序列。所以your class name应该改为TNEntry。在下面的代码中,请记住我已将类名更改为TNEntry

我还改变了这堂课的一些内容。在getInstance(Object obj)方法中,spcrange字段的类型不正确(根据ASN1定义,它们都是sequences): p>

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
}

我只是不知道如何处理 one 字段,因为它没有标记。也许它应该是DERIA5String,或者可能还有另一种类型的“未标记”选项。

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

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(只是为了让事情更容易一点):

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();
    }
}

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

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),你可以这样做:

// 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

注意事项:

  • 正如我所说,这段代码不完整,因为我不确定如何处理 TNEntryone 字段,因为它没有标记(我不知道它是否必须是DERIA5String 或者如果“未标记”字段存在其他类型的对象)。
  • 您还可以做一些改进:
    • ServiceProviderCodeList 可以有 1 到 3 个元素,因此您可以验证其大小
    • TelephoneNumberRangestart 字段具有特定格式(FROM ("0123456789#*"),我认为这意味着只接受这些字符),因此您也可以对其进行验证
    • 要创建ServiceProviderCodeListTelephoneNumberRange 的值,我已经手动创建了DERSequence 对象,但是如果需要,您可以为它们创建自定义类:ServiceProviderCodeList 可以保存@987654357 的列表@ 并在其构造函数中执行正确的验证(大小从 1 到 3),TelephoneNumberRange 可以有 startcount 字段(正确验证 start value) - 而toASN1Primitive 只需要以正确的顺序返回其字段的DERSequence

对于您的parsing issues,我检查了acme4j code,它使用java.security.cert.X509Certificate 类。此类的toString() 方法(使用Sun 的默认提供程序时)正在生成此“扩展未知”输出(根据相应的code)。

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

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

【讨论】:

  • 你是天才雨果。我只想用你的语言感谢你,Muito obrigado:D。我的证书中有扩展名。但它适用于自签名证书权利。不适用于 Lets-encrypt CA 签名证书。我希望服务器会忽略这个扩展!而且我仍然收到ObjectId: 1.3.6.1.5.5.7.1.26 Criticality=false **Extension unknown: DER encoded OCTET string =** 0000: 04 25 30 23 A0 14 16 08 33 39 20 61 62 20 32 36 .%0#....39 ab 26 0010: 16 08 32 31 20 63 36 20 30 31 A1 0B 16 06 31 32 ..21 c3 456 为什么它不识别这个扩展?
  • 不客气。你得到“扩展未知”,因为解析证书的系统没有使用你的新类。获得可读输出的唯一方法是使用这些类(当然,如果您可以访问它,请尝试找到此代码并更改它)
  • Hugo,如何以及在何处添加我的课程。我找不到任何解析我的证书的地方。是用 CertificateFactory 还是 JcaX509v3CertificateBuilder 完成的?我使用 acme4j 作为客户端。这是该客户端环境的链接github.com/shred/acme4j
  • 也许在某个地方调用了toString 类的toString 方法。无论如何,我稍后会看看。目前,您如何使用 acme4j 类?
【解决方案2】:

由于 OID 1.3.6.1.5.5.7.1.26 仍然是一个草案,我认为像 Let's Encrypt 这样的工具和系统不太可能识别这个扩展(他们可能会这个扩展成为官方之后这样做,我真的不知道这种批准背后的官僚程序。

这意味着您可能必须对其进行编码。我已经使用 Bouncy Castle 几年了,但从未创建过新的 ASN1 结构。但如果必须的话,我会看看它的源代码作为初步指导。

考虑到这个扩展的 ASN1 结构:

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

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

 ServiceProviderCodeList ::= SEQUENCE SIZE (1..3) OF IA5String

 -- Service Provider Codes may be OCNs, various SPIDs, or other
 -- SP identifiers from the telephone network

 TelephoneNumberRange ::= SEQUENCE {
   start E164Number,
   count INTEGER
   }

 E164Number ::= IA5String (SIZE (1..15)) (FROM ("0123456789#*"))

扩展值必须是TNEntry 中的SEQUENCE。所以你可以使用ASN1Sequence(或其子类DERSequence)并将TNEntry的实例放入其中。

要创建TNEntry,您需要实现ASN1Choice(查看GeneralName 类的源代码并执行类似操作)。

等等,直到你将整个结构映射到它们各自的类,使用 Bouncy Castle 内置类来支持你(IA5StringDERIntegerDERIA5String对于INTEGER,可用于ServiceProviderCodeListTelephoneNumberRange)

之后,您可以构建自己的解析器,它可以识别此扩展。但正如我所说,不要指望其他工具能够识别它。

【讨论】:

  • 非常感谢雨果。抱歉,我没有 15 个声望来投票支持您的评论!!我为此 OID 创建了一个新的 ASN1 结构。我将在下面发布代码。我对此毫无疑问。您提到的其他扩展(例如 GeneralName)在其他类中引用,例如 PKIHeaderBuilderX509CertificateObjectX509ExtentionUtil 等。我是否需要像其他类似课程中的其他人一样添加这个新扩展?并且您提到了构建自己的解析器,请您简要说明一下。
  • 我认为你不需要。我只是建议查看GeneralName 以了解如何使用 Bouncy Castle 类实现自定义 ASN1 结构。由于TNAuthorizationList 仍然是草稿,因此您无需在任何地方添加它。我要做的是只在需要的地方处理这个扩展(例如解析它的值,添加到证书,以及你可能有的任何用例)
  • 通过“构建你自己的解析器”我的意思是像 openssl 和类似的工具不会识别这个扩展(他们不会像其他的那样显示它的名字 - 比如 KeyusageSubject Alternative Names 等等)。因此,如果您想以可读的方式显示此扩展值及其名称 (TNAuthorizationList) 及其所有字段(spc、range...),则必须编写自定义解析器来执行此操作(但这会更容易,因为您已经创建了 ASN1 结构)
  • 是的。扩展值名称和字段名称应在证书中可读。我必须为此做些什么。因为,我运行我自己的 Boulder 服务器实例并修改了代码以接受这个新扩展。但是此扩展名打印为 Certificate Extensions: 10 [1]: ObjectId: 1.3.6.1.5.5.7.1.26 Criticality=false Extension unknown: DER encoded OCTET string = 0000: 04 13 30 11 13 0F 53 50 49 44 20 3A 20 33 39 20 ..0...SPC : dc 2b 之类的内容。你有任何示例代码或任何帮助来创建这个解析器,这对我很有帮助。
  • 在这种情况下,我认为您应该修改读取扩展的代码。如果您使用ASN1Object obj = X509ExtensionUtil.fromExtensionValue(cert.getExtensionValue("1.3.6.1.5.5.7.1.26")),则obj 将是DERSequence(致电obj.getClass() 进行确认)。那么obj就是TNAuthorizationList对应的ASN1结构,你就可以相应地解析了(你说自定义的ASN1结构已经创建好了,就用它来解析这个obj
【解决方案3】:

现在,出于测试目的,我只是从我的 CA Boulder 传递一个字符串值。所以要阅读,这是 TNAUthList 的自定义 ASN1 对象结构。

public class TNAuthorizationList extends ASN1Object implements ASN1Choice{

public static final int spc                     = 0;
public static final int range                   = 1;

private ASN1Encodable obj;
private int           tag;

public TNAuthorizationList(
        int           tag,
        ASN1Encodable name)
    {
        this.obj = name;
        this.tag = tag;
    }

public TNAuthorizationList(
        int       tag,
        String    name)
    {
        this.tag = tag;

        if (tag == spc)
        {
            this.obj = new DERIA5String(name);
        }
        else if (tag == range)
        {
            this.obj = new ASN1ObjectIdentifier(name);
        }
        else
        {
            throw new IllegalArgumentException("can't process String for tag: " + tag);
        }
    }

public static TNAuthorizationList getInstance(
        Object obj)
    {
        if (obj == null || obj instanceof TNAuthorizationList)
        {
            return (TNAuthorizationList)obj;
        }

        if (obj instanceof ASN1TaggedObject)
        {
            ASN1TaggedObject    tagObj = (ASN1TaggedObject)obj;
            int                 tag = tagObj.getTagNo();

            switch (tag)
            {
            case spc:
                return new TNAuthorizationList(tag, DERIA5String.getInstance(tagObj, false));
            }
        }

        if (obj instanceof byte[])
        {
            try
            {
                return getInstance(ASN1Primitive.fromByteArray((byte[])obj));
            }
            catch (IOException e)
            {
                throw new IllegalArgumentException("unable to parse encoded general name");
            }
        }

        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
    }

public static TNAuthorizationList getInstance(
        ASN1TaggedObject tagObj,
        boolean          explicit)
    {
        return TNAuthorizationList.getInstance(ASN1TaggedObject.getInstance(tagObj, true));
    }

    public int getTagNo()
    {
        return tag;
    }

    public ASN1Encodable getSpc()
    {
        return obj;
    }

    public String toString()
    {
        StringBuffer buf = new StringBuffer();

        buf.append(tag);
        buf.append(": ");
        switch (tag)
        {
        case spc:
            buf.append(DERIA5String.getInstance(obj).getString());
            break;
        default:
            buf.append(obj.toString());
        }
        return buf.toString();
    }



/**
*TNEntry ::= CHOICE {
*       spc   [0] ServiceProviderCodeList,
*       range [1] TelephoneNumberRange,
*       one       E164Number
*       }
*/
@Override
public ASN1Primitive toASN1Primitive() {
    // TODO Auto-generated method stub
    return new DERTaggedObject(false, tag, obj);
}

}

正如您所建议的,我已将 OID 值传递给 X509Util 类并打印输出。

ASN1Object o = X509ExtensionUtil.fromExtensionValue(cert.getExtensionValue("1.3.6.1.5.5.7.1.26"));
    System.out.println("ASN1 Object: "+o);
    System.out.println("get Class "+o.getClass());

O/P 是

ASN1 Object: [SPID : 39 dc 2b]
get Class class org.bouncycastle.asn1.DLSequence

这样好吗。如何使用我的自定义 ASN1 结构解析这个?

【讨论】:

    【解决方案4】:

    这就是我使用 ACME4j 的方式。

    public class RSASignedCertificate {
    
    private static final int KEY_SIZE = 2048;
    
    private static final Logger LOG = Logger.getLogger(CCIDClient.class);
    
    @SuppressWarnings("unused")
    public void fetchCertificate(String domain,String spid, String email, int port,
            String username, String password, String certPath) throws Exception {
        // Load or create a key pair for the user's account
        boolean createdNewKeyPair = true;
        KeyPair domainKeyPair = null;
    
        DomainKeyStore details = null;
        KeyPair userKeyPair = null;
    
        userKeyPair = KeyPairUtils.createKeyPair(KEY_SIZE);
    
        DateFormat dateTime = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        Date date;
        details = new DomainKeyStore();
    
        // Create Hibernate Util class Object
    
        // dao=new HibernateDAO();
        boolean isDomainExist = new HibernateDAO().isDomainExist(domain);
        if (isDomainExist) {
    
            details.setDomain(domain);
            details.setEmail(email);
            date = new Date();
            details.setUpdatedOn(dateTime.parse(dateTime.format(date)));
            boolean updateresult = new HibernateDAO().updateDetails(details);
    
            LOG.info("User Details Updated ");
        }
    
        else {
    
            date = new Date();
            // Date currentDateTime = dateTime.parse(dateTime.format(date));
            details.setEmail(email);
            details.setDomain(domain);
            details.setStatus("Not Registered");
            details.setCreatedOn(dateTime.parse(dateTime.format(date)));
            details.setUpdatedOn(dateTime.parse(dateTime.format(date)));
    
            boolean isInserted = new HibernateDAO().insertDetails(details);
            if (!isInserted) {
                throw new AcmeException("Unable to insert details");
            }
            LOG.info("User Details inserted ");
        }
        // details=dao.getDetails(domain);
    
        Session session = null;
        if (userKeyPair != null) {
            session = new Session("http://192.168.1.143:4000/directory", userKeyPair);
            System.out.println(session.getServerUri().toString());
            System.out.println(session.resourceUri(Resource.NEW_REG));
        }
        Registration reg = null;
        try {
            reg = new RegistrationBuilder().create(session);
            LOG.info("Registered a new user, URI: " + reg.getLocation());
        } catch (AcmeConflictException ex) {
            reg = Registration.bind(session, ex.getLocation());
            LOG.info("Account does already exist, URI: " + reg.getLocation());
        }
        date = new Date();
        details.setStatus("Registered");
        details.setRegistrationDate(dateTime.parse(dateTime.format(date)));
        details.setUpdatedOn(dateTime.parse(dateTime.format(date)));
    
        new HibernateDAO().updateRegistration(details);
    
        URI agreement = reg.getAgreement();
        LOG.info("Terms of Service: " + agreement);
    
        if (createdNewKeyPair) {
            boolean accepted = acceptAgreement(reg, agreement);
            if (!accepted) {
                return;
            }
        }
    
        Authorization auth = null;
        try {
            auth = reg.authorizeDomain(spid);
        } catch (AcmeUnauthorizedException ex) {
            // Maybe there are new T&C to accept?
            boolean accepted = acceptAgreement(reg, agreement);
            if (!accepted) {
                return;
            }
            // Then try again...
            auth = reg.authorizeDomain(spid);
        }
        LOG.info("New authorization for domain " + spid);
        LOG.info("Authorization " + auth);
    
        Challenge challenge = tokenChallenge(auth);
        // System.out.println("Challendg status before trigger :"+challenge.getStatus());
        if (challenge == null) {
            throw new AcmeException("No Challenge found");
        }
    
        if (challenge.getStatus() == Status.VALID) {
            return;
        }
        challenge.trigger();
        int attempts = 1;
        // System.out.println("Challendg status after trigger :"+challenge.getStatus());
        while (challenge.getStatus() != Status.VALID && attempts-- > 0) {
            // System.out.println(challenge.getStatus());
            if (challenge.getStatus().equals(Status.PENDING)) {
                challenge.update();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    LOG.warn("interrupted", e);
                    e.printStackTrace();
                }
            }
            if (challenge.getStatus() == Status.INVALID) {
                LOG.error("Challenge failed... Giving up.");
                throw new AcmeServerException("Challenge Failed");
            }
            try {
                Thread.sleep(3000L);
            } catch (InterruptedException ex) {
                LOG.warn("interrupted", ex);
            }
            challenge.update();
        }
        if (challenge.getStatus() != Status.VALID) {
            LOG.error("Failed to pass the challenge... Giving up.");
            throw new AcmeServerException("Challenge Failed");
        }
    
        date = new Date();
        details.setStatus("Clallenge Completed");
        details.setUpdatedOn(dateTime.parse(dateTime.format(date)));
        new HibernateDAO().updateChallenge(details);
    
        domainKeyPair = KeyPairUtils.createKeyPair(KEY_SIZE);
    
        // Generate a CSR for the domain
        CSRBuilder csrb = new CSRBuilder();
        csrb.addDomains(spid);
        csrb.sign(domainKeyPair);
    
        // System.out.println("CSR:" +csrb.getCSR());
    
        LOG.info("Keys Algorithm: "
                + domainKeyPair.getPrivate().getAlgorithm());
    
        PrivateKeyStore privatekey = new PrivateKeyStore();
        privatekey.setDomain(spid);
        privatekey.setEmail(email);
        privatekey.setPrivateKey(domainKeyPair.getPrivate().getEncoded());
    
        PublicKeyStore publickey = new PublicKeyStore();
        publickey.setDomain(spid);
        publickey.setEmail(email);
        publickey.setPublicKey(domainKeyPair.getPublic().getEncoded());
    
            // Request a signed certificate
        Certificate certificate = reg.requestCertificate(csrb.getEncoded());
        LOG.info("Success! The certificate for spids " + spid
                + " has been generated!");
        LOG.info("Certificate URI: " + certificate.getLocation());
    
        String nameFile = spid.replace(".", "") + ".cer";
    
        X509Certificate sscert = CertificateUtils.createTlsSniCertificate(domainKeyPair,spid);
    
        System.out.println("Certificate :" +sscert);
    
        ASN1Primitive o = X509ExtensionUtil.fromExtensionValue(sscert.getExtensionValue(TNAuthorizationList.TN_AUTH_LIST_OID.getId()));
        System.out.println("ASN1:Object "+o+" class: "+o.getClass());
        TNAuthorizationList TNList = TNAuthorizationList.getInstance(o);
        System.out.println(TNList.toString());
    
        File createFile = new File(certPath + nameFile);
        if (!createFile.exists()) {
            createFile.createNewFile();
        }
    
        try (FileWriter fw = new FileWriter(createFile.getAbsoluteFile())) {
            CertificateUtils.writeX509Certificate(sscert, fw);
            System.out.println("Certificate " + sscert);
            System.out.println("Certificate Content" + fw);
        }
    
        date = new Date();
        Calendar c = Calendar.getInstance();
        c.setTime(new Date());
        c.add(Calendar.DATE, 90);
        details.setIssueDate(dateTime.parse(dateTime.format(date)));
        details.setUpdatedOn(dateTime.parse(dateTime.format(date)));
        details.setValidUntil(dateTime.parse(dateTime.format(c.getTime())));
        details.setStatus("Issued");
    
    
    
        details.setCertPath(certPath + nameFile);
        new HibernateDAO().updateCertificate(details);
    
    }
    
    public boolean acceptAgreement(Registration reg, URI agreement) throws AcmeException
             {
    
        reg.modify().setAgreement(agreement).commit();
        LOG.info("Updated user's ToS");
    
        return true;
    }
    
    public Challenge tokenChallenge(Authorization auth)
    {
        TokenChallenge chall = auth.findChallenge(TokenChallenge.TYPE);
    
        LOG.info("File name: " + chall.getType());
        //LOG.info("Content: " + chall.`);
        return chall;
    
    }
    

    【讨论】:

    • 我想打印“扩展名未知”的行是这样的:System.out.println("Certificate " + sscert); 对吧?可能CertificateUtils.createTlsSniCertificate() 使用 SUN 默认的提供程序创建它。然后,System.out.println 调用证书的toString 方法,该方法无法识别扩展名。如果我是对的,那么您需要将此代码替换为自定义代码(使用您的类)以获得格式化输出(就像您在System.out.println(TNList.toString()) 中所做的那样)。我不确定最好的方法,也许你可以创建一个类来打印所有证书字段..
    • ...或者您必须复制一些代码(Sun 的提供程序或 bouncycastle)并包含自定义代码来打印此扩展名(我会这样做...)。明天我会尝试提供一些代码,但同时尝试这种方法。
    • 好的,雨果。我会试试这个。
    猜你喜欢
    • 1970-01-01
    • 2018-06-13
    • 1970-01-01
    • 1970-01-01
    • 2016-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多