使用CertPathBuilder构建认证路径时无法获得受信任的根CA [英] Can't get trusted root CA when building certification path with CertPathBuilder

查看:123
本文介绍了使用CertPathBuilder构建认证路径时无法获得受信任的根CA的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个针对最终实体,一些中间CA和某些受信任的CA的证书,并且我试图使用 CertPathBuilder 查找最终实体与以下实体之间的证书路径可信CA之一。但是,我当前的实现包括任何中间CA和最终实体,但是没有包括受信任的根。

I have certificates for an end entity, some intermediate CAs and some trusted CAs, and I'm trying to use CertPathBuilder to find the certification path between the end entity and one of the trusted CAs. However, my current implementation is including any intermediate CAs and the end entity, but failing to include the trusted root.

我已经尝试了BouncyCastle提供程序( CertPathBuilder。 getInstance( PKIX, BC))和Sun的( CertPathBuilder.getInstance( PKIX)),但我得到了相同的结果。

I have tried both the BouncyCastle provider (CertPathBuilder.getInstance("PKIX", "BC")) and Sun's (CertPathBuilder.getInstance("PKIX")), but I get the same result.

这是一个使用Bouncy Castle的独立Kotlin代码段( implementation( org.bouncycastle:bcpkix-jdk15on:1.66))来生成证书。我的路径构建函数是 buildCertificationPath

Here's a self-contained Kotlin snippet, using Bouncy Castle (implementation("org.bouncycastle:bcpkix-jdk15on:1.66")) to generate the certificates. My path building function is 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,但是我希望能赢得

As a workaround, I may end up computing the root by iterating over the trusted CAs until I find the one that issued the last certificate in the path, but I'm hoping that won't be necessary.

推荐答案

投射 pathBuilderResult java.security.cert.PKIXCertPathBuilderResult ( PKIX的实现需要返回实现此结果的结果)。然后,您会发现getTrustAnchor()方法可用,并返回用作此结果的TA的证书。

Cast pathBuilderResult to java.security.cert.PKIXCertPathBuilderResult (implementations of "PKIX" are required to return a result implementing this). You'll then find the getTrustAnchor() method is available, returning the certificate that served as TA for this result.

这篇关于使用CertPathBuilder构建认证路径时无法获得受信任的根CA的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆