在我生成的证书中添加新的扩展名 [英] Adding a new Extension to my generated certificate
问题描述
我需要在证书中添加新的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 $ c更改为$ c>返回更易读的字符串的方法:
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 aDERIA5String
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 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 it- To create the values for
ServiceProviderCodeList
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 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. ThetoString()
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 newTNAuthorizationList
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屋!
-