美文网首页
如何验证证书链的签出关系

如何验证证书链的签出关系

作者: CodingCode | 来源:发表于2020-06-24 00:56 被阅读0次

    如何验证证书链的签出关系

    在证书链中,通常由根CA证书,签出中间CA证书,再签出服务证书。这是一个签出链关系:
    Root CA -> Intermediate CA1 -> Intermediate CA2 -> ... Intermediate CAn -> Server Certificate

    那么如何验证谁是谁的签出关系呢,意思就是验证一个证书是由谁签发的。
    通常我们拿到一个完整服务端证书的时候会包含:

    1. certificate.pem // 证书PEM
    2. key.pem // 私钥PEM
    3. ca-chain.pem // CA PEM

    ca-chain.pem里面可能包含多个CA证书,如何找到直接签出那个CA证书呢。

    我们查看证书的一般内容:

    $ $ openssl x509 -text -noout -in cert.pem
        Signature Algorithm: ecdsa-with-SHA256
            Issuer: CN=<cn>, OUT=<ou>, O=<o>, L=<l>, ST=<st>, C=<c>
            Validity
                Not Before: Jun 23 00:13:00 2020 GMT
                Not After : Jun 23 00:18:00 2023 GMT
            Subject: CN=<cn1>, OUT=<ou1>, O=<o>, L=<l>, ST=<st>, C=<c>
            Subject Public Key Info:
                Public Key Algorithm: id-ecPublicKey
                    Public-Key: (256 bit)
                    pub: 
                        04:f5:XXXX:aa:a9:
                        b3:6d:XXXX:95:1f:
                        30:70:XXXX:44:19:
                        70:e5:XXXX:fb:54:
                        b1:8a:e8:be:bf
                    ASN1 OID: prime256v1
                    NIST CURVE: P-256
        ...
        Signature Algorithm: ecdsa-with-SHA256
             30:45:XXXX:c3:e8:
             1a:54:XXXX:01:e1:
             55:02:XXXX:bb:b2:
             45:af:XXXX:d1:d3
    

    有几个信息:

    1. Issuer:就是谁签发的,签发者的Subject。
    2. Subject:当前证书的Subject。
    3. Public-Key:证书包含的public key。

    这里Issuer就明确表明了当前证书是由谁签发的,那我们去找ca-chain里面的所有证书,挨个遍历找出其Subject和这个一样,那么就说明是签发证书。

    理论上是这样的,但是Subject只是包含文本信息,用户可以篡改伪造,只要是同名Subject就可以啦。所以这个字段只是便于肉眼辨识使用,真正使用验证的是signature信息。

    在前面证书内容中,最后我们看到有Signature Algorithm域,它的值就是签出证书的signature,这个签名是根据签出证书的内容生成的,只有签出证书本身才能得出这个signature。所以我们需要对照CA证书,生成他的signature值,然后和服务端证书里的这个域值进行比较,如果相当就说明是签出证书。

    这个算法比较复杂,不细究了,golang里面提供了函数可以直接调用:
    func (c *Certificate) CheckSignatureFrom(parent *Certificate) error

    举例:

    package main
    
    import (
        "fmt"
        "os"
        "io/ioutil"
        "crypto/x509"
        "encoding/pem"
    )
    
    func loadCertificate(pemFile string) (*x509.Certificate, error) {
        certBuff, err := ioutil.ReadFile(pemFile)
        if err != nil {
            fmt.Printf("ERROR: failed to read keystore file: %s, error: %s\n", pemFile, err)
            return nil, err
        }
    
        block, _ := pem.Decode(certBuff)
        if block == nil {
            fmt.Printf("ERROR: block of decoded private key is nil\n")
            return nil, err
        }
    
        cert, err := x509.ParseCertificate(block.Bytes)
        if err != nil {
            fmt.Printf("ERROR: failed get ECDSA private key, error: %v\n", err)
            return nil, err
        }
    
        return cert, nil
    }
    
    func main() {
        cert, err   := loadCertificate(os.Args[1])
        if err != nil {
            fmt.Printf("ERROR: failed to load cert, error: %v\n", err)
            return
        }
        issuer, err := loadCertificate(os.Args[2])
        if err != nil {
            fmt.Printf("ERROR: failed to load issuer, error: %v\n", err)
            return
        }
        err = cert.CheckSignatureFrom(issuer)
        if err != nil {
            fmt.Printf("ERROR: verify failed, error: %v\n", err)
            return
        }
        fmt.Printf("cert   subject: %s\n", cert.Subject)
        fmt.Printf("cert   issuer : %s\n", cert.Issuer)
        fmt.Printf("issuer Subject: %s\n", issuer.Subject)
    }
    

    输入:go build test.go && ./test cert.pem ca.pem
    验证ca.pem是不是cert.pem的直接签出证书。

    还有一个办法是使用: func (Certificate) Verify() 函数。
    如果verify成功,在返回的chains [][]
    Certificate中每一列的:

    1. 第一个certificate是自己
    2. 第二个certificate就是直接签出证书。
    3. 最后一个certificate是Roots里面的一个证书。

    相关文章

      网友评论

          本文标题:如何验证证书链的签出关系

          本文链接:https://www.haomeiwen.com/subject/uthlfktx.html