扩展名未知:生成的证书中的 DER 编码 OCTET 字符串错误

Extension unknown: DER encoded OCTET string error in generated certificate

我已经使用 acme 客户端 acme4j 创建了一个 SSL 证书:https://github.com/shred/acme4j

但是当我生成自签名证书时,我在解析它时遇到异常。这是我生成的证书:

{Version: V3
Subject: T=spid: yuz8xxz
Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

Key:  Sun RSA public key, 2048 bits
modulus: 

public exponent: 65537
Validity: [From: Mon Apr 10 17:56:36 IST 2017,
           To: Mon Apr 17 17:56:36 IST 2017]
Issuer: T=spid: yuz8xxz
SerialNumber: [    015b57d4 807c]

Certificate Extensions: 1
[1]: ObjectId: 1.3.6.1.5.5.7.1.26 Criticality=false


**Extension unknown: DER encoded OCTET string =**
0000: 04 23 30 21 A0 12 16 07   79 75 7A 38 78 78 7A 16  .#0!....yuz8xxz.
0010: 07 79 75 7A 38 78 78 7A   A1 0B 16 06 31 32 33 34  .yuz8xxz....1234
0020: 35 36 02 01 01                                     56...


]
 Algorithm: [SHA256withRSA]
 Signature:
]
}

1.3.6.1.5.5.7.1.26 似乎是电话号码(TN)授权列表的OID,仅在草稿文档中定义(https://datatracker.ietf.org/doc/draft-ietf-stir-certificates/); BouncyCastle 可能没有漂亮的打印机,所以它向您展示了原始编码的有效负载。

虽然我不是电话专家,但我很确定 ('yuz8xxz'、'yuz8xxz') 不是有效的服务提供商代码列表,并且“123456”- 123456' 不是有效的电话号码范围。所以目前还不清楚您要使用此证书扩展寻找什么,如果 Let's Encrypt 签署了它,我会感到非常惊讶。

Acme4j 代码使用了java.security.cert.X509Certificateclass。此 class 的 toString() 方法(使用 Sun 的默认提供程序 时)正在生成此 "extension unknown" 输出(根据corresponding code)。

因此,为了正确解析它(获得格式化输出),您可能必须更改 acme4j 的代码(或编写您自己的代码),包括解析此扩展的代码。

在我的测试中(Java 7BouncyCastle 1.56),我为 X509Certificate 创建了一个包装器并基于 BouncyCastle 的代码创建了一个 format 方法(我复制了大部分内容,只是为 TNAuthorizationList 扩展添加了代码)。下面的代码不是最佳的(异常处理不佳和一些已弃用的 classes),但你可以得到一个想法。

import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.misc.*;
import org.bouncycastle.asn1.util.ASN1Dump;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.util.encoders.Hex;

public class CertificateWrapper {

    private X509Certificate cert;

    public CertificateWrapper(X509Certificate cert) {
        this.cert = cert;
    }

    public String format() throws Exception {
        StringBuffer buf = new StringBuffer();
        String nl = System.getProperty("line.separator");

        buf.append("  [0]         Version: ").append(this.cert.getVersion()).append(nl);
        buf.append("         SerialNumber: ").append(this.cert.getSerialNumber()).append(nl);
        buf.append("             IssuerDN: ").append(this.cert.getIssuerDN().toString()).append(nl);
        buf.append("           Start Date: ").append(this.cert.getNotBefore()).append(nl);
        buf.append("           Final Date: ").append(this.cert.getNotAfter()).append(nl);
        buf.append("            SubjectDN: ").append(this.cert.getSubjectDN().toString()).append(nl);
        buf.append("           Public Key: ").append(this.cert.getPublicKey()).append(nl);
        buf.append("  Signature Algorithm: ").append(this.cert.getSigAlgName()).append(nl);

        byte[] sig = this.cert.getSignature();

        buf.append("            Signature: ").append(new String(Hex.encode(sig, 0, 20))).append(nl);
        for (int i = 20; i < sig.length; i += 20) {
            if (i < sig.length - 20) {
                buf.append("                       ").append(new String(Hex.encode(sig, i, 20))).append(nl);
            } else {
                buf.append("                       ").append(new String(Hex.encode(sig, i, sig.length - i))).append(nl);
            }
        }

        TBSCertificateStructure tbs = TBSCertificateStructure.getInstance(ASN1Sequence.fromByteArray(cert.getTBSCertificate()));
        X509Extensions extensions = tbs.getExtensions();

        if (extensions != null) {
            Enumeration e = extensions.oids();

            if (e.hasMoreElements()) {
                buf.append("       Extensions: \n");
            }

            while (e.hasMoreElements()) {
                ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) e.nextElement();
                X509Extension ext = extensions.getExtension(oid);

                if (ext.getValue() != null) {
                    byte[] octs = ext.getValue().getOctets();
                    ASN1InputStream dIn = new ASN1InputStream(octs);
                    buf.append("                       critical(").append(ext.isCritical()).append(") ");
                    try {
                        if (oid.equals(Extension.basicConstraints)) {
                            buf.append(BasicConstraints.getInstance((ASN1Sequence) dIn.readObject())).append(nl);
                        } else if (oid.equals(Extension.keyUsage)) {
                            buf.append(KeyUsage.getInstance((DERBitString) dIn.readObject())).append(nl);
                        } else if (oid.equals(MiscObjectIdentifiers.netscapeCertType)) {
                            buf.append(new NetscapeCertType((DERBitString) dIn.readObject())).append(nl);
                        } else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL)) {
                            buf.append(new NetscapeRevocationURL((DERIA5String) dIn.readObject())).append(nl);
                        } else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension)) {
                            buf.append(new VerisignCzagExtension((DERIA5String) dIn.readObject())).append(nl);
                        //*********************************************************
                        // *** HERE: code to handle TNAuthorizationList ***
                        //*********************************************************
                        } else if (oid.equals(TNAuthorizationList.TN_AUTH_LIST_OID)) {
                            buf.append(TNAuthorizationList.getInstance((ASN1Sequence) dIn.readObject())).append(nl);
                        } else {
                            buf.append(oid.getId());
                            buf.append(" value = ").append(ASN1Dump.dumpAsString(dIn.readObject())).append(nl);
                        }
                    } catch (Exception ex) {
                        buf.append(oid.getId());
                        buf.append(" value = ").append("*****").append(nl);
                    }
                } else {
                    buf.append(nl);
                }
            }
        }

        return buf.toString();
    }
}

然后,您只需使用此包装器而不是 X509Certificate:

X509Certificate cert = ...
System.out.println("Certificate " + new CertificateWrapper(cert).format());

输出格式与 SUN 的默认提供程序不同,但您可以自定义它以获得您需要的内容。


PS:本题是对. The OP's code was provided and TNAuthorizationList class is in the 的补充(或"sequel")。但由于这个问题是一个不同的问题(相关但不同),它被保留为一个单独的问题。