如何验证链中的所有证书结合起来以创建到受信任的根 CA 证书的信任链?

How to validate that all certificates in the chain combine to create a chain of trust to a trusted root CA certificate?

因此,我正在尝试手动验证来自我正在开发的 Alexa Skill 的请求,但无法“验证链中的所有证书结合起来以创建到受信任的根 CA 证书的信任链。”如 documentation.

的步骤 3.c 所示

处理这些证书对我来说是全新的领域,因为我已经在不使用亚马逊建议的 ask-sdk 的情况下构建了这项技能,所以我认为手动实施验证而不是调整我的技能以使用开发工具包。但现在我被困住了,需要帮助。

我不太明白 caStore 是如何工作的,我想我可能没有正确地初始化它。我尝试使用技能请求中提供的亚马逊签名证书和两个根证书(代码中的网址)但没有成功。

这是代码

const rp = require('request-promise')
const pki = require('node-forge').pki

const AlexaSkill = {
    validate: async (request) => {

        if (request.headers) {            
            
            const signature = request.headers.signature
            const signatureCertChainUrl = request.headers.signaturecertchainurl
            if (signature && signatureCertChainUrl) {
                
                let urlPieces = signatureCertChainUrl.replace('../', '').split('echo.api/')
                if (urlPieces.length > 1) {
                    
                    const normalizedUrl = `${urlPieces[0]}echo.api/${urlPieces[urlPieces.length - 1]}`
                    if (normalizedUrl.startsWith('https://s3.amazonaws.com:443/echo.api/') || normalizedUrl.startsWith('https://s3.amazonaws.com/echo.api/')) {
                        
                        const pem = await rp(signatureCertChainUrl)
                        const amazonSigningPem = pem.substring(0, pem.indexOf('END CERTIFICATE-----\n') + 21)
                        const amazonSigningCert = pki.certificateFromPem(amazonSigningPem)

                        const pem1 = await rp('https://www.amazontrust.com/repository/AmazonRootCA1.pem')
                        const amazonRootCert1 = pki.certificateFromPem(pem1)                        
                        const pem2 = await rp('https://www.amazontrust.com/repository/AmazonRootCA2.pem')
                        const amazonRootCert2 = pki.certificateFromPem(pem2)
                        // const pem3 = await rp('https://www.amazontrust.com/repository/AmazonRootCA3.pem')
                        // const amazonRootCert3 = pki.certificateFromPem(pem3)
                        // const pem4 = await rp('https://www.amazontrust.com/repository/AmazonRootCA4.pem')
                        // const amazonRootCert4 = pki.certificateFromPem(pem4)

                        const caStore = pki.createCaStore([ amazonSigningCert ])
                        const caStore1 = pki.createCaStore([ amazonRootCert1 ])
                        const caStore2 = pki.createCaStore([ amazonRootCert2 ])
                        // const caStore3 = pki.createCaStore([ amazonRootCert3 ])
                        // const caStore4 = pki.createCaStore([ amazonRootCert4 ])

                        const certChain = pem.substring(pem.indexOf('END CERTIFICATE-----\n') + 21)
                            .split('-----END CERTIFICATE-----\n')
                            .filter(cert => cert.length > 0)
                            .map(cert => pki.certificateFromPem(`${cert}-----END CERTIFICATE-----\n`))

                        try {
                            const v = pki.verifyCertificateChain(caStore, certChain)
                            console.log('Passed!', v)
                        } catch (e) {
                            console.log('Error!', JSON.stringify(e))
                        }

                        try {
                            const v1 = pki.verifyCertificateChain(caStore1, certChain)
                            console.log('Passed!', v1)
                        } catch (e) {
                            console.log('Error!', JSON.stringify(e))
                        }

                        try {
                            const v2 = pki.verifyCertificateChain(caStore2, certChain)
                            console.log('Passed!', v2)
                        } catch (e) {
                            console.log('Error!', JSON.stringify(e))
                        }
                    }
                }
            }
        }

        return false
    }
}

和输出

Error! {"message":"Certificate is not trusted.","error":"forge.pki.UnknownCertificateAuthority"}

Error! {"message":"Certificate is not trusted.","error":"forge.pki.UnknownCertificateAuthority"}

Error! {"message":"Certificate is not trusted.","error":"forge.pki.UnknownCertificateAuthority"}

提前致谢。

设法使用这个正确地实现它:https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/5822660bf6914a9aac60cde55e18542b3080de93/ask-sdk-express-adapter/lib/verifier/index.ts

这是一个示例,说明我如何解决它,实现我的中间件,该中间件将在每个亚马逊请求上调用:

//https://s3.amazonaws.com/echo.api/echo-api-cert-8.pem
const certChain = 
`-----BEGIN CERTIFICATE-----
MIIFbjCCBFagAwIBAgIQBRsSsK9/f9dI7rlZGtPo3TANBgkqhkiG9w0BAQsFADBG
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRUwEwYDVQQLEwxTZXJ2ZXIg
Q0EgMUIxDzANBgNVBAMTBkFtYXpvbjAeFw0yMDAzMTkwMDAwMDBaFw0yMTAzMTAx
MjAwMDBaMB4xHDAaBgNVBAMTE2VjaG8tYXBpLmFtYXpvbi5jb20wggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmd8SQClJ92SBjL2Q3G1MxQ4QEnTVE7qA3
QhsAphIe1FHkSt4IVODhugprFNecdYppWSGy0Bzl7kn0KGer4NE6TJ3KV/Ux4jzw
M1aXhq3JmQcOA7DFO+EqdjhcwSoVmZJF32M7X0M2AdR3HZB9s/dk8MDpEPewtT4/
2V0CsbEsA9sgKnzVZUdbL3SETLPJyh/+8N84fm32mjkrQdFPVqGWK9fp1tYwE+3M
5X6ZiiCXJsnyewnxOFm1upiNbX/6hJtMJch9kpIfQzz8eKHt9jtLpsM5m04pV/t8
SmQlQ+rnnH/JrDN5HJ86D6nAW3o8UcILwGTDnsre18OJ3AbFxkBXAgMBAAGjggJ+
MIICejAfBgNVHSMEGDAWgBRZpGYGUqB7lZI8o5QHJ5Z0W/k90DAdBgNVHQ4EFgQU
C+rUYvMdvL3ZR8OQM3yvONiDbZUwHgYDVR0RBBcwFYITZWNoby1hcGkuYW1hem9u
LmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUF
BwMCMDsGA1UdHwQ0MDIwMKAuoCyGKmh0dHA6Ly9jcmwuc2NhMWIuYW1hem9udHJ1
c3QuY29tL3NjYTFiLmNybDAgBgNVHSAEGTAXMAsGCWCGSAGG/WwBAjAIBgZngQwB
AgEwdQYIKwYBBQUHAQEEaTBnMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5zY2Ex
Yi5hbWF6b250cnVzdC5jb20wNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQuc2NhMWIu
YW1hem9udHJ1c3QuY29tL3NjYTFiLmNydDAMBgNVHRMBAf8EAjAAMIIBAwYKKwYB
BAHWeQIEAgSB9ASB8QDvAHUAu9nfvB+KcbWTlCOXqpJ7RzhXlQqrUugakJZkNo4e
0YUAAAFw8nrysgAABAMARjBEAiAVBY5M1ilaFGHsa5K84rYLBNQ6F8uk1k3JXVj4
H0iIjgIgDFq0tTjV4Og5gs+NpUqZpLrr3glIFPLnqrx+/0S+vtYAdgBc3EOS/uar
RUSxXprUVuYQN/vV+kfcoXOUsl7m9scOygAAAXDyevLgAAAEAwBHMEUCIQDbA/GW
VyYTwK+8DuLEIu2hL2/ZJyrK4oxW+tLqhHlJdgIgLWsr6aTfpkV37kNwPGU/nTFC
XZJ4kTSx8LM+flSvivQwDQYJKoZIhvcNAQELBQADggEBAJRWyWcxgKYiy2Zej3Oa
2Xlju+I7YZAtFqD9at6SxUm/HNJ9h9Faclv927q0MDvHesYvIshJwMqTz2Vad9jC
burAboHup9HCP9vPP0sHB1CUveifu0FcxpmpEr6LWISf3Jl0bTTYGhfJFZ1H+L/R
N5XefXdqFJg5PY5A0Ex8ajBNW9/sSzAuiemm9Ijose6P8g4q9ydgSfOaiasyjdTh
ySVOhyKspsBm6FsYySlBukUHWdEUnbCuRBljy6ES5TmY0YGAXzV+sBiSC2iMgMWh
HeATCmBpbaKkFKrR+WHKcYGKHaVsiXA/acVlIhKTyA1d7HQP0y4cytcOQUvhr4Bw
x3o=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIESTCCAzGgAwIBAgITBn+UV4WH6Kx33rJTMlu8mYtWDTANBgkqhkiG9w0BAQsF
ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
b24gUm9vdCBDQSAxMB4XDTE1MTAyMjAwMDAwMFoXDTI1MTAxOTAwMDAwMFowRjEL
MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEVMBMGA1UECxMMU2VydmVyIENB
IDFCMQ8wDQYDVQQDEwZBbWF6b24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDCThZn3c68asg3Wuw6MLAd5tES6BIoSMzoKcG5blPVo+sDORrMd4f2AbnZ
cMzPa43j4wNxhplty6aUKk4T1qe9BOwKFjwK6zmxxLVYo7bHViXsPlJ6qOMpFge5
blDP+18x+B26A0piiQOuPkfyDyeR4xQghfj66Yo19V+emU3nazfvpFA+ROz6WoVm
B5x+F2pV8xeKNR7u6azDdU5YVX1TawprmxRC1+WsAYmz6qP+z8ArDITC2FMVy2fw
0IjKOtEXc/VfmtTFch5+AfGYMGMqqvJ6LcXiAhqG5TI+Dr0RtM88k+8XUBCeQ8IG
KuANaL7TiItKZYxK1MMuTJtV9IblAgMBAAGjggE7MIIBNzASBgNVHRMBAf8ECDAG
AQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUWaRmBlKge5WSPKOUByeW
dFv5PdAwHwYDVR0jBBgwFoAUhBjMhTTsvAyUlC4IWZzHshBOCggwewYIKwYBBQUH
AQEEbzBtMC8GCCsGAQUFBzABhiNodHRwOi8vb2NzcC5yb290Y2ExLmFtYXpvbnRy
dXN0LmNvbTA6BggrBgEFBQcwAoYuaHR0cDovL2NydC5yb290Y2ExLmFtYXpvbnRy
dXN0LmNvbS9yb290Y2ExLmNlcjA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3Js
LnJvb3RjYTEuYW1hem9udHJ1c3QuY29tL3Jvb3RjYTEuY3JsMBMGA1UdIAQMMAow
CAYGZ4EMAQIBMA0GCSqGSIb3DQEBCwUAA4IBAQCFkr41u3nPo4FCHOTjY3NTOVI1
59Gt/a6ZiqyJEi+752+a1U5y6iAwYfmXss2lJwJFqMp2PphKg5625kXg8kP2CN5t
6G7bMQcT8C8xDZNtYTd7WPD8UZiRKAJPBXa30/AbwuZe0GaFEQ8ugcYQgSn+IGBI
8/LwhBNTZTUVEWuCUUBVV18YtbAiPq3yXqMB48Oz+ctBWuZSkbvkNodPLamkB2g1
upRyzQ7qDn1X8nn8N8V7YJ6y68AtkHcNSRAnpTitxBKjtKPISLMVCx7i4hncxHZS
yLyKQXhw2W2Xs0qLeC1etA+jTGDK4UfLeC0SF7FSi8o5LL21L8IzApar2pR/
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgITBn+USionzfP6wq4rAfkI7rnExjANBgkqhkiG9w0BAQsF
ADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNj
b3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4x
OzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1
dGhvcml0eSAtIEcyMB4XDTE1MDUyNTEyMDAwMFoXDTM3MTIzMTAxMDAwMFowOTEL
MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
jgSubJrIqg0CAwEAAaOCATEwggEtMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
BAQDAgGGMB0GA1UdDgQWBBSEGMyFNOy8DJSULghZnMeyEE4KCDAfBgNVHSMEGDAW
gBScXwDfqgHXMCs4iKK4bUqc8hGRgzB4BggrBgEFBQcBAQRsMGowLgYIKwYBBQUH
MAGGImh0dHA6Ly9vY3NwLnJvb3RnMi5hbWF6b250cnVzdC5jb20wOAYIKwYBBQUH
MAKGLGh0dHA6Ly9jcnQucm9vdGcyLmFtYXpvbnRydXN0LmNvbS9yb290ZzIuY2Vy
MD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly9jcmwucm9vdGcyLmFtYXpvbnRydXN0
LmNvbS9yb290ZzIuY3JsMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG9w0BAQsF
AAOCAQEAYjdCXLwQtT6LLOkMm2xF4gcAevnFWAu5CIw+7bMlPLVvUOTNNWqnkzSW
MiGpSESrnO09tKpzbeR/FoCJbM8oAxiDR3mjEH4wW6w7sGDgd9QIpuEdfF7Au/ma
eyKdpwAJfqxGF4PcnCZXmTA5YpaP7dreqsXMGz7KQ2hsVxa81Q4gLv7/wmpdLqBK
bRRYh5TmOTFffHPLkIhqhBGWJ6bt2YFGpn6jcgAKUj6DiAdjd4lpFw85hdKrCEVN
0FE6/V1dN2RMfjCyVSRCnTawXZwXgWHxyvkQAiSr6w10kY17RSlQOYiypok1JR4U
akcjMS9cmvqtmg5iUaQqqcT5NJ0hGA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEdTCCA12gAwIBAgIJAKcOSkw0grd/MA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNV
BAYTAlVTMSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTIw
MAYDVQQLEylTdGFyZmllbGQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
eTAeFw0wOTA5MDIwMDAwMDBaFw0zNDA2MjgxNzM5MTZaMIGYMQswCQYDVQQGEwJV
UzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTElMCMGA1UE
ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjE7MDkGA1UEAxMyU3RhcmZp
ZWxkIFNlcnZpY2VzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVDDrEKvlO4vW+GZdfjohTsR8/
y8+fIBNtKTrID30892t2OGPZNmCom15cAICyL1l/9of5JUOG52kbUpqQ4XHj2C0N
Tm/2yEnZtvMaVq4rtnQU68/7JuMauh2WLmo7WJSJR1b/JaCTcFOD2oR0FMNnngRo
Ot+OQFodSk7PQ5E751bWAHDLUu57fa4657wx+UX2wmDPE1kCK4DMNEffud6QZW0C
zyyRpqbn3oUYSXxmTqM6bam17jQuug0DuDPfR+uxa40l2ZvOgdFFRjKWcIfeAg5J
Q4W2bHO7ZOphQazJ1FTfhy/HIrImzJ9ZVGif/L4qL8RVHHVAYBeFAlU5i38FAgMB
AAGjgfAwge0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0O
BBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMB8GA1UdIwQYMBaAFL9ft9HO3R+G9FtV
rNzXEMIOqYjnME8GCCsGAQUFBwEBBEMwQTAcBggrBgEFBQcwAYYQaHR0cDovL28u
c3MyLnVzLzAhBggrBgEFBQcwAoYVaHR0cDovL3guc3MyLnVzL3guY2VyMCYGA1Ud
HwQfMB0wG6AZoBeGFWh0dHA6Ly9zLnNzMi51cy9yLmNybDARBgNVHSAECjAIMAYG
BFUdIAAwDQYJKoZIhvcNAQELBQADggEBACMd44pXyn3pF3lM8R5V/cxTbj5HD9/G
VfKyBDbtgB9TxF00KGu+x1X8Z+rLP3+QsjPNG1gQggL4+C/1E2DUBc7xgQjB3ad1
l08YuW3e95ORCLp+QCztweq7dp4zBncdDQh/U90bZKuCJ/Fp1U1ervShw3WnWEQt
8jxwmKy6abaVd38PMV4s/KCHOkdp8Hlf9BRUpJVeEXgSYCfOn8J3/yNTd126/+pZ
59vPr5KW7ySaNRB6nJHGDn2Z9j8Z3/VyVOEVqQdZe4O/Ui5GjLIAZHYcSNPYeehu
VsyuLAOQ1xk4meTKCRlb/weWsKh/NEnfVqn3sF/tM+2MR7cwA130A4w=
-----END CERTIFICATE-----`;

然后我解析这 4 个证书:

const pki = require('node-forge').pki;
// ....
//parse bundle certificate
    let certificatesPem = [];
    let currentCert = [];
    var arrayOfLines = certChain.split("\n")

for (_i = 0; _i < arrayOfLines.length; _i++) {
    line = arrayOfLines[_i];
    if (!(line.length !== 0)) {
      continue;
    }
    currentCert.push(line);
    if (line.match(/-END CERTIFICATE-/)) {
        certificatesPem.push(currentCert.join("\n"));
      currentCert = [];
    }
}

现在我的所有证书都在 certificatesPem[]

接下来我解码这些证书:

//decode each sertificate
let certDecoded= []
for(i = 0 ; i <  certificatesPem.length  ; i++)
{
    certDecoded[i] = pki.certificateFromPem(certificatesPem[i]);
}

然后我使用 pki.verify() 方法检查链

//check chain of trust , 1 Link at a time
let ChainLink = false
for(i = certificatesPem.length-1 ; i >0  ; i--)
{
    try {
        ChainLink = certDecoded[i].verify(certDecoded[i-1]); // this method throws an error instead of false 
        //console.log('chain '+ i +' ok')   
        console.log( certDecoded[i].subject.attributes[i].value + " ==> " + certDecoded[i-1].subject.attributes[i-1].value + " : ok")   
    } catch (error) {
        console.log('certification chain verification failed ')
        return res.status(400).send({ 
        error: ' invalid certification chain ! at link index ' + i
        });

    }
}

预期输出:

Starfield Technologies, Inc. ==> Amazon Root CA 1 : ok
Amazon Root CA 1 ==> Amazon : ok
Amazon ==> echo-api.amazon.com : ok

调试证书的有用工具: https://hohnstaedt.de/xca/