我有一个针对最终实体,一些中间CA和一些受信任的CA的证书,并且我试图CertPathBuilder用来查找最终实体和其中一个受信任的CA之间的证书路径。但是,我当前的实现包括任何中间CA和最终实体,但是没有包括受信任的根。
我已经尝试了BouncyCastle提供程序(CertPathBuilder.getInstance(“PKIX”, “BC”))和Sun的(CertPathBuilder.getInstance(“PKIX”)),但是得到的结果相同。
这是一个独立的Kotlin代码段,使用Bouncy Castle(implementation(“org.bouncycastle:bcpkix-jdk15on:1.66”))生成证书。我的路径构建功能是buildCertificationPath。
package com.example.cert import org.bouncycastle.asn1.ASN1Boolean import org.bouncycastle.asn1.ASN1Encodable import org.bouncycastle.asn1.ASN1EncodableVector import org.bouncycastle.asn1.ASN1Integer import org.bouncycastle.asn1.ASN1Primitive import org.bouncycastle.asn1.DERBMPString import org.bouncycastle.asn1.DEROctetString import org.bouncycastle.asn1.DERSequence import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500NameBuilder import org.bouncycastle.asn1.x500.style.BCStyle import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier import org.bouncycastle.asn1.x509.Extension import org.bouncycastle.asn1.x509.SubjectKeyIdentifier import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.cert.X509v3CertificateBuilder import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder import java.math.BigInteger import java.security.KeyPair import java.security.KeyPairGenerator import java.security.MessageDigest import java.security.PrivateKey import java.security.PublicKey import java.security.SecureRandom import java.security.Security import java.security.cert.CertPathBuilder import java.security.cert.CertPathBuilderException import java.security.cert.CertStore import java.security.cert.CollectionCertStoreParameters import java.security.cert.PKIXBuilderParameters import java.security.cert.PKIXParameters import java.security.cert.TrustAnchor import java.security.cert.X509CertSelector import java.security.cert.X509Certificate import java.sql.Date import java.time.ZonedDateTime val bcToJavaCertificateConverter: JcaX509CertificateConverter = JcaX509CertificateConverter().setProvider(BouncyCastleProvider()) fun main() { Security.addProvider(BouncyCastleProvider()) // Issue certificates using BouncyCastle val rootCAKeyPair = generateRSAKeyPair() val rootCACert = issueCertificate( "root", rootCAKeyPair.public, rootCAKeyPair.private, isCA = true, pathLenConstraint = 2 ) val intermediateCAKeyPair = generateRSAKeyPair() val intermediateCACert = issueCertificate( "intermediate", intermediateCAKeyPair.public, rootCAKeyPair.private, rootCACert, isCA = true, pathLenConstraint = 1 ) val endEntityKeyPair = generateRSAKeyPair() val endEntityCert = issueCertificate( "end", endEntityKeyPair.public, intermediateCAKeyPair.private, intermediateCACert, isCA = false, pathLenConstraint = 0 ) // Convert BouncyCastle certificates to Java ones: val javaRootCert = convertBCCertToJava(rootCACert) val javaInterCert = convertBCCertToJava(intermediateCACert) val javaEndCert = convertBCCertToJava(endEntityCert) val intermediateAndRootPath = buildCertificationPath( javaInterCert, emptySet(), setOf(javaRootCert) ) if (intermediateAndRootPath.contentEquals(arrayOf("intermediate", "root"))) { println("Path between intermediate and root CA is OK") } else { println( "Path between intermediate and root CA is wrong: " + intermediateAndRootPath.joinToString(",") ) } val endAndIntermediatePath = buildCertificationPath( javaEndCert, emptySet(), setOf(javaInterCert) ) if (endAndIntermediatePath.contentEquals(arrayOf("end", "intermediate"))) { println("Path between end entity and intermediate CA is OK") } else { println( "Path between end entity and intermediate CA is wrong: " + endAndIntermediatePath.joinToString(",") ) } val endAndRootPath = buildCertificationPath( javaEndCert, setOf(javaInterCert), setOf(javaRootCert) ) if (endAndRootPath.contentEquals(arrayOf("end", "intermediate", "root"))) { println("Path between end entity and root CA is OK") } else { println("Path between end entity and root CA is wrong: " + endAndRootPath.joinToString(",")) } } fun buildCertificationPath( endEntityCert: X509Certificate, intermediateCACerts: Set<X509Certificate>, trustedCACerts: Set<X509Certificate> ): Array<String> { val trustAnchors = trustedCACerts.map { TrustAnchor(it, null) }.toSet() val intermediateCertStore = CertStore.getInstance( "Collection", CollectionCertStoreParameters(intermediateCACerts), "BC" ) val endEntitySelector = X509CertSelector() endEntitySelector.certificate = endEntityCert val parameters: PKIXParameters = PKIXBuilderParameters(trustAnchors, endEntitySelector) parameters.isRevocationEnabled = false // TODO: Needed? parameters.addCertStore(intermediateCertStore) // val pathBuilder: CertPathBuilder = CertPathBuilder.getInstance("PKIX") val pathBuilder: CertPathBuilder = CertPathBuilder.getInstance("PKIX", "BC") val pathBuilderResult = try { pathBuilder.build(parameters) } catch (exc: CertPathBuilderException) { exc.printStackTrace() return emptyArray() } val certificates = pathBuilderResult.certPath.certificates return certificates.map { X509CertificateHolder(it.encoded).subject.getRDNs(BCStyle.CN).first().first.value.toString() }.toTypedArray() } fun generateRSAKeyPair(): KeyPair { val keyGen = KeyPairGenerator.getInstance("RSA") keyGen.initialize(2048) return keyGen.generateKeyPair() } fun issueCertificate( subjectCommonName: String, subjectPublicKey: PublicKey, issuerPrivateKey: PrivateKey, issuerCertificate: X509CertificateHolder? = null, isCA: Boolean = false, pathLenConstraint: Int = 0 ): X509CertificateHolder { val subjectDistinguishedName = buildDistinguishedName(subjectCommonName) val issuerDistinguishedName = if (issuerCertificate != null) issuerCertificate.subject else subjectDistinguishedName val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(subjectPublicKey.encoded) val now = ZonedDateTime.now() val builder = X509v3CertificateBuilder( issuerDistinguishedName, generateRandomBigInteger(), Date.from(now.toInstant()), Date.from(now.plusDays(1).toInstant()), subjectDistinguishedName, subjectPublicKeyInfo ) val basicConstraints = BasicConstraintsExtension( isCA, pathLenConstraint ) builder.addExtension(Extension.basicConstraints, true, basicConstraints) val subjectPublicKeyDigest = getSHA256Digest(subjectPublicKeyInfo.encoded) val issuerSKI = issuerCertificate?.getExtension(Extension.subjectKeyIdentifier) if (issuerSKI != null) { val aki = AuthorityKeyIdentifier((issuerSKI.parsedValue as DEROctetString).octets) builder.addExtension(Extension.authorityKeyIdentifier, false, aki) } val ski = SubjectKeyIdentifier(subjectPublicKeyDigest) builder.addExtension(Extension.subjectKeyIdentifier, false, ski) val signerBuilder = JcaContentSignerBuilder("SHA256WITHRSAANDMGF1").build(issuerPrivateKey) return builder.build(signerBuilder) } fun convertBCCertToJava(bcCert: X509CertificateHolder): X509Certificate = bcToJavaCertificateConverter.getCertificate(bcCert) fun generateRandomBigInteger(): BigInteger { val random = SecureRandom() return BigInteger(64, random) } fun buildDistinguishedName(commonName: String): X500Name { val builder = X500NameBuilder(BCStyle.INSTANCE) builder.addRDN(BCStyle.CN, DERBMPString(commonName)) return builder.build() } fun getSHA256Digest(input: ByteArray): ByteArray { val digest = MessageDigest.getInstance("SHA-256") return digest.digest(input) } class BasicConstraintsExtension( private val cA: Boolean, private val pathLenConstraint: Int ) : ASN1Encodable { init { if (pathLenConstraint < 0 || 2 < pathLenConstraint) { throw Exception( "pathLenConstraint should be between 0 and 2 (got $pathLenConstraint)" ) } if (pathLenConstraint != 0 && !cA) { throw Exception( "Subject should be a CA if pathLenConstraint=$pathLenConstraint" ) } } override fun toASN1Primitive(): ASN1Primitive { val sequence = ASN1EncodableVector(2) sequence.add(ASN1Boolean.getInstance(cA)) sequence.add(ASN1Integer(pathLenConstraint.toLong())) return DERSequence(sequence) } }
这是我得到的输出:
Path between intermediate and root CA is wrong: intermediate Path between end entity and intermediate CA is wrong: end Path between end entity and root CA is wrong: end,intermediate
作为一种解决方法,我可能会通过迭代受信任的CA来最终计算出根,直到找到路径中发布了最后一个证书的CA,但是我希望这不是必需的。