从终端实体获取根证书和中间证书
Getting root and intermediate certificates from an end-entity
仍然是密码学的菜鸟,我每天都会偶然发现一些简单的事情。而今天只是其中之一。
我想用 bouncy castle 库验证 java 中的 smime 消息,我想我差不多已经搞定了,但目前的问题是 PKIXparameters 对象的构建。
比方说,我有一个具有以下结构的终端实体 x509 证书:
root certificate
+->intermediate certificate
+->end-entity certificate
为了验证消息,我需要先建立信任链,但我不知道如何从终端实体中提取根证书和中间证书。
我尝试使用终端实体作为根,但没有成功:
InputStream isCert = GetFISCertificate();
List list = new ArrayList();
X509Certificate rootCert = (X509Certificate) certificateFactory.generateCertificate(isCert);
list.add(rootCert);
CollectionCertStoreParameters params = new CollectionCertStoreParameters(list);
CertStore store = CertStore.getInstance("Collection", params, BC);
//create cert path
List certChain = new ArrayList();
certChain.add(rootCert);
CertPath certPath = certificateFactory.generateCertPath(certChain);
Set trust = Collections.singleton(new TrustAnchor(rootCert, null));
//validation
CertPathValidator certPathValidator = CertPathValidator.getInstance("PKIX", BC);
PKIXParameters pKIXParameters = new PKIXParameters(trust);
pKIXParameters.addCertStore(store);
pKIXParameters.setDate(new Date());
try {
CertPathValidatorResult result = certPathValidator.validate(certPath, pKIXParameters);
System.out.println("certificate path validated");
} catch (CertPathValidatorException e) {
System.out.println("validation failed on certificate number " + e.getIndex() + ", details: " + e.getMessage());
}
得到这个异常:
validation failed on certificate number -1, details: Trust anchor for certification path not found.
顺便说一句,我可以只使用最终实体证书来验证消息,就像它是自签名证书一样吗?
我使用 BouncyCastle 1.56 进行了此测试。
从最终实体获取颁发者证书的一种方法是查找 Authority Information Access extension.
此扩展 可能 存在(这不是强制性的)并且 可能 包含 URL 以获取颁发者的证书( issuer 是当前证书“之上”的证书,因此最终实体的颁发者是中间体,中间体的颁发者是根。
您可以使用 BouncyCastle 获取此扩展值:
import java.security.cert.X509Certificate;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.x509.extension.X509ExtensionUtil;
X509Certificate cert = // end entity certificate
// get Authority Information Access extension (will be null if extension is not present)
byte[] extVal = cert.getExtensionValue(Extension.authorityInfoAccess.getId());
AuthorityInformationAccess aia = AuthorityInformationAccess.getInstance(X509ExtensionUtil.fromExtensionValue(extVal));
// check if there is a URL to issuer's certificate
AccessDescription[] descriptions = aia.getAccessDescriptions();
for (AccessDescription ad : descriptions) {
// check if it's a URL to issuer's certificate
if (ad.getAccessMethod().equals(X509ObjectIdentifiers.id_ad_caIssuers)) {
GeneralName location = ad.getAccessLocation();
if (location.getTagNo() == GeneralName.uniformResourceIdentifier) {
String issuerUrl = location.getName().toString();
// http URL to issuer (test in your browser to see if it's a valid certificate)
// you can use java.net.URL.openStream() to create a InputStream and create
// the certificate with your CertificateFactory
URL url = new URL(issuerUrl);
X509Certificate issuer = (X509Certificate) certificateFactory.generateCertificate(url.openStream());
}
}
}
因此您可以将此代码与最终实体证书一起使用来获取中间件。然后你再次使用它与中间体一起获得根。
然后您将 root 添加到您的 TrustAnchor
并且验证应该有效。
注意:但正如我所说,此扩展名不是强制性的,可能不会存在。在这种情况下,getExtensionValue
将 return null
,我知道的唯一选择是在 google 中搜索证书并下载它们(这些证书链通常是 public 而且不难找到)
顺便说一句,如果我们在 windows 中安装了证书,一切都会简单得多:
KeyStore ks = KeyStore.getInstance("Windows-MY");
ks.load(null, null);
String alias = "your alias";
ArrayList<X509Certificate> certsChain = new ArrayList<>();
if (ks.isCertificateEntry(alias)) {
Certificate[] chain = ks.getCertificateChain(alias);
System.out.println("Chain length: " + chain.length);
for(Certificate c : chain) certsChain.add((X509Certificate)c);
}
Collections.reverse(certsChain);
certsChain.forEach(MainClass::printDBG);
繁荣,整个证书链准备就绪
仍然是密码学的菜鸟,我每天都会偶然发现一些简单的事情。而今天只是其中之一。
我想用 bouncy castle 库验证 java 中的 smime 消息,我想我差不多已经搞定了,但目前的问题是 PKIXparameters 对象的构建。 比方说,我有一个具有以下结构的终端实体 x509 证书:
root certificate
+->intermediate certificate
+->end-entity certificate
为了验证消息,我需要先建立信任链,但我不知道如何从终端实体中提取根证书和中间证书。
我尝试使用终端实体作为根,但没有成功:
InputStream isCert = GetFISCertificate();
List list = new ArrayList();
X509Certificate rootCert = (X509Certificate) certificateFactory.generateCertificate(isCert);
list.add(rootCert);
CollectionCertStoreParameters params = new CollectionCertStoreParameters(list);
CertStore store = CertStore.getInstance("Collection", params, BC);
//create cert path
List certChain = new ArrayList();
certChain.add(rootCert);
CertPath certPath = certificateFactory.generateCertPath(certChain);
Set trust = Collections.singleton(new TrustAnchor(rootCert, null));
//validation
CertPathValidator certPathValidator = CertPathValidator.getInstance("PKIX", BC);
PKIXParameters pKIXParameters = new PKIXParameters(trust);
pKIXParameters.addCertStore(store);
pKIXParameters.setDate(new Date());
try {
CertPathValidatorResult result = certPathValidator.validate(certPath, pKIXParameters);
System.out.println("certificate path validated");
} catch (CertPathValidatorException e) {
System.out.println("validation failed on certificate number " + e.getIndex() + ", details: " + e.getMessage());
}
得到这个异常:
validation failed on certificate number -1, details: Trust anchor for certification path not found.
顺便说一句,我可以只使用最终实体证书来验证消息,就像它是自签名证书一样吗?
我使用 BouncyCastle 1.56 进行了此测试。
从最终实体获取颁发者证书的一种方法是查找 Authority Information Access extension.
此扩展 可能 存在(这不是强制性的)并且 可能 包含 URL 以获取颁发者的证书( issuer 是当前证书“之上”的证书,因此最终实体的颁发者是中间体,中间体的颁发者是根。
您可以使用 BouncyCastle 获取此扩展值:
import java.security.cert.X509Certificate;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.x509.extension.X509ExtensionUtil;
X509Certificate cert = // end entity certificate
// get Authority Information Access extension (will be null if extension is not present)
byte[] extVal = cert.getExtensionValue(Extension.authorityInfoAccess.getId());
AuthorityInformationAccess aia = AuthorityInformationAccess.getInstance(X509ExtensionUtil.fromExtensionValue(extVal));
// check if there is a URL to issuer's certificate
AccessDescription[] descriptions = aia.getAccessDescriptions();
for (AccessDescription ad : descriptions) {
// check if it's a URL to issuer's certificate
if (ad.getAccessMethod().equals(X509ObjectIdentifiers.id_ad_caIssuers)) {
GeneralName location = ad.getAccessLocation();
if (location.getTagNo() == GeneralName.uniformResourceIdentifier) {
String issuerUrl = location.getName().toString();
// http URL to issuer (test in your browser to see if it's a valid certificate)
// you can use java.net.URL.openStream() to create a InputStream and create
// the certificate with your CertificateFactory
URL url = new URL(issuerUrl);
X509Certificate issuer = (X509Certificate) certificateFactory.generateCertificate(url.openStream());
}
}
}
因此您可以将此代码与最终实体证书一起使用来获取中间件。然后你再次使用它与中间体一起获得根。
然后您将 root 添加到您的 TrustAnchor
并且验证应该有效。
注意:但正如我所说,此扩展名不是强制性的,可能不会存在。在这种情况下,getExtensionValue
将 return null
,我知道的唯一选择是在 google 中搜索证书并下载它们(这些证书链通常是 public 而且不难找到)
顺便说一句,如果我们在 windows 中安装了证书,一切都会简单得多:
KeyStore ks = KeyStore.getInstance("Windows-MY");
ks.load(null, null);
String alias = "your alias";
ArrayList<X509Certificate> certsChain = new ArrayList<>();
if (ks.isCertificateEntry(alias)) {
Certificate[] chain = ks.getCertificateChain(alias);
System.out.println("Chain length: " + chain.length);
for(Certificate c : chain) certsChain.add((X509Certificate)c);
}
Collections.reverse(certsChain);
certsChain.forEach(MainClass::printDBG);
繁荣,整个证书链准备就绪