如何从Base64字符串中提取DER编码证书

3

需要帮助解码以下base64 MerkleTreeLeaf字符串。

MerkleTreeLeaf结构是一个复合数据结构,包含时间戳和数字证书。

该结构被编码为Base64编码的字节字符串。在这个字节字符串中,有一个实际的证书以DER格式编码。

我正在寻找一个PHP解决方案,以提取DER编码的证书。

以下是Base64编码字符串的样例:

AAAAAAFNDPxFcQAAAAUaMIIFFjCCBLygAwIBAgIQM+cDs5qlvsI+p1fkebkn3TAKBggqhkjOPQQDAjCBizELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMTwwOgYDVQQDEzNTeW1hbnRlYyBDbGFzcyAzIEVDQyAyNTYgYml0IEV4dGVuZGVkIFZhbGlkYXRpb24gQ0EwHhcNMTQxMjA4MDAwMDAwWhcNMTUxMjA4MjM1OTU5WjCCARExEzARBgsrBgEEAYI3PAIBAxMCVVMxGTAXBgsrBgEEAYI3PAIBAgwIRGVsYXdhcmUxGzAZBgsrBgEEAYI3PAIBAQwKV2lsbWluZ3RvbjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxHTAbBgNVBA8TFFByaXZhdGUgT3JnYW5pemF0aW9uMRAwDgYDVQQFEwcyMTU4MTEzMRYwFAYDVQQKDA1TeW1hbnRlYyBDb3JwMR0wGwYDVQQLDBRmb3IgY3QgdGVzdGluZy0tIGVjYzEgMB4GA1UEAwwXZXZwcm8xLmV2MS5zeW1hbnRlYy5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdGXgRQD4CxlJr1QU7hZ86gbPxjlCHWDBZBSb5+vbo9bw2sZxaxwF15mHiSlEiaGVuwv2DxOPO+w/QVgMTggZ5o4ICdzCCAnMwIgYDVR0RBBswGYIXZXZwcm8xLmV2MS5zeW1hbnRlYy5jb20wCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCB4AwZgYDVR0gBF8wXTBbBgtghkgBhvhFAQcXBjBMMCMGCCsGAQUFBwIBFhdodHRwczovL2Quc3ltY2IuY29tL2NwczAlBggrBgEFBQcCAjAZDBdodHRwczovL2Quc3ltY2IuY29tL3JwYTArBgNVHR8EJDAiMCCgHqAchhpodHRwOi8vc24uc3ltY2IuY29tL3NuLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHwYDVR0jBBgwFoAUSBNlF5TsnhYqKnRc6FMttPuD644wVwYIKwYBBQUHAQEESzBJMB8GCCsGAQUFBzABhhNodHRwOi8vc24uc3ltY2QuY29tMCYGCCsGAQUFBzAChhpodHRwOi8vc24uc3ltY2IuY29tL3NuLmNydDCCAQIGCisGAQQB1nkCBAIEgfMEgfAA7gB1AKS5CZC0GFgUh7sTosxncAo8NZgE+RvfuON3zQ7IDdwQAAABSitKXtQAAAQDAEYwRAIgIQ2Dr+ktUJh1OFYAJxUqHwMjLmsMdvGToFaxSqEcAg8CIDjgHlQ8mV/NXsUC115hF5S7MZirjD7mdH52s6yv5JQiAHUAOTdvVF97Rgf1l0LXaM1dJDe/NHO2U0pINLz3Lmgcg8kAAAFKK0phCQAABAMARjBEAiA1GlOmlOeAP+uOmq+uPMVeN0TDsRZBxUE7oWUtZlKBOAIgFOoXuDXnAFALmW7CN66yrnHO7UyuMYy5t4oJqvgmzzowCgYIKoZIzj0EAwIDSAAwRQIgNqNb4f3YfKCvnkYjihpB9jGq0iUnfPclyD7mWxDJYt8CIQCJbJ48ZxGx2WYGRMXys52lxc/VxzzvdlSRto9y/jLhzAAA

如果我使用在线base64转换工具,它会显示一些细节,但显然是不可读的。如果我可以提取DER编码的证书,那么我就可以使用openssl来解析它。


如果有帮助的话,我相信这里有一个Python解决方案 python script,它可能会给出一些线索,但不确定如何在PHP中实现相同的功能。 - user3436467
1个回答

5
你可以采用与你评论中的Python脚本相同的方式,只需比较Python解包文档PHP解包文档。如果你只想要未解析的证书,你可以使用以下方法:
<?php

function mtl_to_x509($base64str) {
    $raw = base64_decode($base64str);
    // Parse the decoded string
    $cert_length = unpack('N', chr(0).substr($raw, 12, 3))[1];
    $cert_as_asn1 = substr($raw, 15, $cert_length);
    return $cert_as_asn1;
}

$example = "AAAAAAFNDPxFcQAAAAUaMIIFFjCCBLygAwIBAgIQM+cDs5qlvsI+p1fkebkn3TAKBggqhkjOPQQDAjCBizELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMTwwOgYDVQQDEzNTeW1hbnRlYyBDbGFzcyAzIEVDQyAyNTYgYml0IEV4dGVuZGVkIFZhbGlkYXRpb24gQ0EwHhcNMTQxMjA4MDAwMDAwWhcNMTUxMjA4MjM1OTU5WjCCARExEzARBgsrBgEEAYI3PAIBAxMCVVMxGTAXBgsrBgEEAYI3PAIBAgwIRGVsYXdhcmUxGzAZBgsrBgEEAYI3PAIBAQwKV2lsbWluZ3RvbjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxHTAbBgNVBA8TFFByaXZhdGUgT3JnYW5pemF0aW9uMRAwDgYDVQQFEwcyMTU4MTEzMRYwFAYDVQQKDA1TeW1hbnRlYyBDb3JwMR0wGwYDVQQLDBRmb3IgY3QgdGVzdGluZy0tIGVjYzEgMB4GA1UEAwwXZXZwcm8xLmV2MS5zeW1hbnRlYy5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdGXgRQD4CxlJr1QU7hZ86gbPxjlCHWDBZBSb5+vbo9bw2sZxaxwF15mHiSlEiaGVuwv2DxOPO+w/QVgMTggZ5o4ICdzCCAnMwIgYDVR0RBBswGYIXZXZwcm8xLmV2MS5zeW1hbnRlYy5jb20wCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCB4AwZgYDVR0gBF8wXTBbBgtghkgBhvhFAQcXBjBMMCMGCCsGAQUFBwIBFhdodHRwczovL2Quc3ltY2IuY29tL2NwczAlBggrBgEFBQcCAjAZDBdodHRwczovL2Quc3ltY2IuY29tL3JwYTArBgNVHR8EJDAiMCCgHqAchhpodHRwOi8vc24uc3ltY2IuY29tL3NuLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHwYDVR0jBBgwFoAUSBNlF5TsnhYqKnRc6FMttPuD644wVwYIKwYBBQUHAQEESzBJMB8GCCsGAQUFBzABhhNodHRwOi8vc24uc3ltY2QuY29tMCYGCCsGAQUFBzAChhpodHRwOi8vc24uc3ltY2IuY29tL3NuLmNydDCCAQIGCisGAQQB1nkCBAIEgfMEgfAA7gB1AKS5CZC0GFgUh7sTosxncAo8NZgE+RvfuON3zQ7IDdwQAAABSitKXtQAAAQDAEYwRAIgIQ2Dr+ktUJh1OFYAJxUqHwMjLmsMdvGToFaxSqEcAg8CIDjgHlQ8mV/NXsUC115hF5S7MZirjD7mdH52s6yv5JQiAHUAOTdvVF97Rgf1l0LXaM1dJDe/NHO2U0pINLz3Lmgcg8kAAAFKK0phCQAABAMARjBEAiA1GlOmlOeAP+uOmq+uPMVeN0TDsRZBxUE7oWUtZlKBOAIgFOoXuDXnAFALmW7CN66yrnHO7UyuMYy5t4oJqvgmzzowCgYIKoZIzj0EAwIDSAAwRQIgNqNb4f3YfKCvnkYjihpB9jGq0iUnfPclyD7mWxDJYt8CIQCJbJ48ZxGx2WYGRMXys52lxc/VxzzvdlSRto9y/jLhzAAA";

print mtl_to_x509($example);

?>

为了验证这个功能,注意我们确实可以使用openssl解析它:
$ php mtl_to_x509.php | openssl x509 -inform der -noout -text | grep Subject:
        Subject: 1.3.6.1.4.1.311.60.2.1.3=US/1.3.6.1.4.1.311.60.2.1.2=Delaware/1.3.6.1.4.1.311.60.2.1.1=Wilmington, C=US, ST=California, L=Mountain View/businessCategory=Private Organization/serialNumber=2158113, O=Symantec Corp, OU=for ct testing-- ecc, CN=evpro1.ev1.symantec.com

如果您需要整个数据结构,相关的解包过程如下:

$version = unpack('C', substr($raw, 0, 1))[1];
$ctype = unpack('C', substr($raw, 1, 1))[1];
$timestamp = unpack('J', substr($raw, 2, 8))[1];
$entry_type = unpack('n', substr($raw, 10, 2))[1];

这个可以工作!但是,对于$version、ctype等,我收到了这个消息:“警告:在32位版本的PHP中不支持64位格式代码的解包(unpack())”。 - user3436467
这可能是 $timestamp 导致的问题,因为在32位上解析无符号长整型将无法工作。要查看值应该是什么,您可以使用 print_r(unpack('C*', substr($raw, 2, 8)));Array( [1] => 0 [2] => 0 [3] => 1 [4] => 77 [5] => 12 [6] => 52 [7] => 69 [8] => 113)。也就是说,时间戳应该是113*256^0 + 69*256^1 + 252*256^2 + 12*256^3 + 77*256^4 + 1\*256^5 = 1430441969009. - fuglede
或者作为两个32位整数:print_r(unpack('N*', substr($raw, 2, 8))); 输出Array( [1] => 333 [2] => 217859441)。并且217859441 + 333*256^4 = 1430441969009. - fuglede
在您的解决方案中,您是如何想到在$cert_length$cert_as_asn1中使用数字12、3和15的?我问这个问题是因为我有另一个Base64字符串,我正在尝试解析它,但它似乎不起作用。您可以在此处找到Base64字符串:https://jpst.it/JgX9 - user3436467
@user3436467:我刚从原帖的评论中提供的示例中获取了它们,但你也可以在CT RFC中找到它们;https://www.ietf.org/rfc/rfc6962.txt - fuglede
感谢您对这个旧的已解决问题的回复。我查看了Python脚本和RFC,但无法确切地看到那些数字,但如果您有时间,我真的很想看看您如何将其应用于另一个字符串。我在这里打开了一个新的线程http://stackoverflow.com/questions/37673634/trouble-unpacking-base64-string - user3436467

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接