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:
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
I want this OID to be recognized similar to other extensions like AuthorityInfoAccess, etc.
Do I need to edit the jar of Bouncy Castle X509 class?
Im using ACME4j as a client and Letsencrypt Boulder as my server.
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();;
}
}
This is how im using ACME4j.
As the OID 1.3.6.1.5.5.7.1.26 is still a draft, I believe it's very unlikely that tools and systems like Let's Encrypt recognize this extension (they'll probably do it after this extension becomes official, and I really don't know the bureaucratic process behind such approvals).
Which means you'll probably have to code it. I've been using Bouncy Castle for a couple of years but never had to create a new ASN1 structure. But if I had to, I'd take a look at its source code as an initial guidance.
Considering the ASN1 structure of this extension:
The extension value must be a
SEQUENCE
ofTNEntry
. So you could useASN1Sequence
(or its subclassDERSequence
) and put instances ofTNEntry
inside it.To create a
TNEntry
, you need to implementASN1Choice
(take a look at source ofGeneralName
class and do something similar).And so on, until you have the whole structure mapped to their respective classes, using Bouncy Castle built-in classes to support you (there are
DERIA5String
forIA5String
andDERInteger
forINTEGER
, which can be used inServiceProviderCodeList
andTelephoneNumberRange
)After that you can build your own parser, which can recognize this extension. But as I said, don't expect other tools to recognize it.
Right now, for testing purpose , Im just passing a string value from my CA Boulder. So to read that, this is the custom ASN1 Object STructure for TNAUthList.
}
As You suggested I have passed the OID value to X509Util class and printed the Output.
and the O/P is
Is this fine. How do i parse this with my custom ASN1 Structure?
Note: for these codes I used bcprov-jdk15on 1.56
Some comments about your code. First of all, note the ASN1 structure:
Note that
TNEntry
is a choice, andTNAuthorizationList
is a sequence ofTNEntry
objects. So your class name should be changed toTNEntry
. In the code below, please remember that I've changed the class name toTNEntry
.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):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.In this same class (remember, I've changed its name to
TNEntry
), I also removed the constructorpublic 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 changedtoString
method to return a more readable string:And I also created a
TNAuthorizationList
class, which holds the sequence ofTNEntry
objects (remember that I've changed your class name toTNEntry
, so thisTNAuthorizationList
class is a different one). Note that I also created a constant to hold the OID (just to make things a little bit easier):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):
Once you have the certificate object (a
X509Certificate
in my example), you can do:The output will be:
Notes:
TNEntry
, because it's not tagged (I don't know if it must be aDERIA5String
or if there's another type of object for an "untagged" field).ServiceProviderCodeList
can have 1 to 3 elements, so you could validate its sizeTelephoneNumberRange
: the start field has a specific format (FROM ("0123456789#*")
which I think it means only these characters are accepted), so you could also validate itServiceProviderCodeList
andTelephoneNumberRange
, I've created theDERSequence
objects by hand, but you can create custom classes for them if you want:ServiceProviderCodeList
could hold a list ofDERIA5String
and perform proper validations in its constructor (size from 1 to 3), andTelephoneNumberRange
could have start and count fields (with proper validation of start value) - andtoASN1Primitive
just need to return aDERSequence
of its fields in the right orderFor your parsing issues, I've checked acme4j code and it uses a
java.security.cert.X509Certificate
class. ThetoString()
method of this class (when using Sun's default provider) is generating this "extension unknown" output (according to the corresponding code).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 newTNAuthorizationList
classes in this method.When you provide the code showing how you're using acme4j, I'll update this answer accordingly, if needed.