Ssl certificate 向生成的证书添加新扩展名
我需要在我的证书中添加OID1.3.6.1.5.5.7.1.26的新扩展。我在证书中获得了此OID扩展,但出现以下错误: 证书延期:10 [1] :ObjectId:1.3.6.1.5.5.7.1.26临界性=假Ssl certificate 向生成的证书添加新扩展名,ssl-certificate,x509certificate,bouncycastle,csr,java-security,Ssl Certificate,X509certificate,Bouncycastle,Csr,Java Security,我需要在我的证书中添加OID1.3.6.1.5.5.7.1.26的新扩展。我在证书中获得了此OID扩展,但出现以下错误: 证书延期:10 [1] :ObjectId:1.3.6.1.5.5.7.1.26临界性=假 扩展名未知:DER编码的八位字节字符串= 0000:04 0C 30 0A 13 08 33 39 20 64 63 20 32 62..0… 39 dc 2b 我希望此OID与其他扩展(如AuthorityInfoAccess等)类似 我需要编辑Bouncy Castle的罐子吗 我
扩展名未知:DER编码的八位字节字符串=
0000:04 0C 30 0A 13 08 33 39 20 64 63 20 32 62..0…
39 dc 2b 我希望此OID与其他扩展(如AuthorityInfoAccess等)类似 我需要编辑Bouncy Castle的罐子吗 我使用ACME4j作为客户端,Letsencrypt Boulder作为服务器 以下是注册证书的CSR生成器代码
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();;
}
}
公共无效符号(密钥对密钥对)引发IOException{
//addProvider(新的BouncyCastleProvider());
对象。requirennull(keypair,“keypair”);
if(namelist.isEmpty()){
抛出新的IllegalStateException(“未设置域”);
}
试一试{
GeneralName[]gns=新的GeneralName[namelist.size()];
对于(intix=0;ix
由于OID1.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
的序列。因此,您可以使用ASN1Sequence
(或其子类DERSequence
)并将TNEntry
的实例放在其中
要创建TNEntry
,您需要实现ASN1Choice
(查看GeneralName
类的源代码并执行类似操作)
依此类推,直到您将整个结构映射到各自的类,使用Bouncy Castle内置类来支持您(对于IA5String
有DERIA5String
,对于INTEGER
有DERInteger
,可用于ServiceProviderCodeList
和TelephoneNumberRange
)
之后,您可以构建自己的解析器,它可以识别此扩展。但是正如我所说的,不要指望其他工具能够识别它。现在,出于测试目的,我只是从我的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结构解析它?注意:对于这些代码,我使用了1.56上的bcprov-jdk15
关于代码的一些注释。首先,请注意ASN1结构:
TNAuthorizationList::=TNEntry的序列大小(1..MAX)
TNEntry::=CHOICE{
spc[0]ServiceProviderCodeList,
范围[1]电话号码排列,
一个E164号码
}
请注意,TNEntry
是一个选择,TNAuthorizationList
是TNEntry
对象的一个序列
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;
}