我意识到这是一个非常类似的职位(比如这个职位),但是这些职位中缺less的细节可能对我的案例有重大意义。
首先,这是我的简化程序:
#include "stdafx.h" #include <windows.h> #include <wincrypt.h> int _tmain(int argc, _TCHAR* argv[]) { // usage: CertExtract certpath char keyFile[] = "C:\\Certificates\\public.crt"; BYTE lp[65536]; SECURITY_ATTRIBUTES sa; HANDLE hKeyFile; DWORD bytes; PCCERT_CONTEXT certContext; sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = FALSE; hKeyFile = CreateFile(keyFile, GENERIC_READ, FILE_SHARE_READ, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hKeyFile) { if (ReadFile(hKeyFile, lp, GetFileSize(hKeyFile, NULL), &bytes, NULL) && bytes > 0) { certContext = CertCreateCertificateContext(X509_ASN_ENCODING, lp, bytes); if (certContext) { printf("yay!"); CertFreeCertificateContext(certContext); } else { printf("Could not convert certificate to internal form\n"); } } else { printf("Failed to read key file: %s\n", keyFile); } } else { printf("Failed to open key file: %s\n", keyFile); } CloseHandle(hKeyFile); return 0; }
为了创build证书,我在OpenSSL中使用了以下步骤:
C:\Certificates>openssl genrsa -out private.key 1024 Loading 'screen' into random state - done Generating RSA private key, 1024 bit long modulus ......................................++++++ ................++++++ e is 65537 (0x10001) C:\Certificates>openssl req -new -key private.key -out public.csr Loading 'screen' into random state - done C:\Certificates>copy private.key private.key.org 1 file(s) copied. C:\Certificates>openssl rsa -in private.key.org -out private.key writing RSA key C:\Certificates>openssl x509 -req -days 365 -in public.csr -signkey private.key -ou t public.crt Loading 'screen' into random state - done Signature ok subject=/CN=My Signing Cert Getting Private key
与以下conf文件:
RANDFILE = .rnd [ req ] distinguished_name = req_distinguished_name prompt = no [ req_distinguished_name ] commonName = My Signing Cert
证书文件如下所示:
-----BEGIN CERTIFICATE----- MIIBqzCCARQCCQDUJyWk0OxlRTANBgkqhkiG9w0BAQUFADAaMRgwFgYDVQQDDA9N eSBTaWduaW5nIENlcnQwHhcNMTYwMTA1MjIzODU5WhcNMTcwMTA0MjIzODU5WjAa MRgwFgYDVQQDDA9NeSBTaWduaW5nIENlcnQwgZ8wDQYJKoZIhvcNAQEBBQADgY0A MIGJAoGBAJobIhfSSMLEPeG9SOBelWHo4hjKXe8dT6cllPr6QXdXe2VNLh9fxVlx spVGFQwjlF3OHYnmSQnY3m2b5wlFNYVuHvy8rUsZWOF4drSbiqWKh0TuJ+4MBeGq EormTJ+kiGqNm5IVRrTu9OV8f0XQTGV1pxHircQxsGhxY5w0QTjjAgMBAAEwDQYJ KoZIhvcNAQEFBQADgYEAedqjKfMyIFC8nUbJ6t/Y8D+fJFwCcdwojUFizr78FEwA IZSas1b1bXSkA+QEooW7pYdBAfzNuD3WfZAIZpqFlr4rPNIqHzYa0OIdDPwzQQLa 3zPKqjj6QeTWEi5/ArzO+sTVv4m3Og3GQjMChb8H/GxsWdbComPVP82DTUet+ZU= -----END CERTIFICATE-----
将PEM编码转换为hex允许我识别证书的各个部分:
30 SEQUENCE //Certificate (82 01 AB) 30 SEQUENCE //tbsCertificate (82 01 14) 02 INTEGER //serialNumber (09) 00 D4 27 25 A4 D0 EC 65 45 30 SEQUENCE //signature (0D) 06 OBJECT IDENTIFIER (09) 2A 86 48 86 F7 0D 01 01 05 05 NULL (00) 30 SEQUENCE //issuer (1A) 31 SET (18) 30 SEQUENCE (16) 06 OBJECT IDENTIFIER (03) 55 04 03 0C UTF8String (0F) 4D 79 20 53 69 67 6E 69 6E 67 20 43 65 72 74 30 SEQUENCE //validity (1E) 17 UTCTime (0D) 31 36 30 31 30 35 32 32 33 38 35 39 5A 17 UTCTime (0D) 31 37 30 31 30 34 32 32 33 38 35 39 5A 30 SEQUENCE //subjectName (1A) 31 SET (18) 30 SEQUENCE (16) 06 OBJECT IDENTIFIER (03) 55 04 03 0C UTF8String (0F) 4D 79 20 53 69 67 6E 69 6E 67 20 43 65 72 74 30 SEQUENCE //subjectPublicKeyInfo (81 9F) 30 SEQUENCE //algorithmId (0D) 06 OBJECT IDENTIFIER //algorithm (09) 2A 86 48 86 F7 0D 01 01 01 05 NULL (00) 03 BIT STRING //subjectPublicKey (81 8D) [00] //padding bits 30 SEQUENCE //RSAPublicKey (81 89) 02 INTEGER //modulus (81 81) 00 9A 1B 22 17 D2 48 C2 C4 3D E1 BD 48 E0 5E 95 61 E8 E2 18 CA 5D EF 1D 4F A7 25 94 FA FA 41 77 57 7B 65 4D 2E 1F 5F C5 59 71 B2 95 46 15 0C 23 94 5D CE 1D 89 E6 49 09 D8 DE 6D 9B E7 09 45 35 85 6E 1E FC BC AD 4B 19 58 E1 78 76 B4 9B 8A A5 8A 87 44 EE 27 EE 0C 05 E1 AA 12 8A E6 4C 9F A4 88 6A 8D 9B 92 15 46 B4 EE F4 E5 7C 7F 45 D0 4C 65 75 A7 11 E2 AD C4 31 B0 68 71 63 9C 34 41 38 E3 02 03 01 00 01 30 SEQUENCE //signatureAlgorithm (0D) 06 OBJECT IDENTIFIER (09) 2A 86 48 86 F7 0D 01 01 05 05 NULL (00) 03 BIT STRING //signatureValue (81 81) [00] //padding bits 79 DA A3 29 F3 32 20 50 BC 9D 46 C9 EA DF D8 F0 3F 9F 24 5C 02 71 DC 28 8D 41 62 CE BE FC 14 4C 00 21 94 9A B3 56 F5 6D 74 A4 03 E4 04 A2 85 BB A5 87 41 01 FC CD B8 3D D6 7D 90 08 66 9A 85 96 BE 2B 3C D2 2A 1F 36 1A D0 E2 1D 0C FC 33 41 02 DA DF 33 CA AA 38 FA 41 E4 D6 12 2E 7F 02 BC CE FA C4 D5 BF 89 B7 3A 0D C6 42 33 02 85 BF 07 FC 6C 6C 59 D6 C2 A2 63 D5 3F CD 83 4D 47 AD F9 95
看起来符合X.509规格 (正如我所预期的那样):
Certificate ::= { tbsCertificate TBSCertificate, signatureAlgorithm AlgorithmIdentifier, signatureValue BIT STRING } TBSCertificate ::= SEQUENCE { version [0] Version DEFAULT v1, <-- what does this mean? serialNumber INTEGER, signature AlgorithmIdentifier, issuer Name, validity Validity, subjectName Name, subjectPublicKeyInfo SubjectPublicKeyInfo ... }
除了版本部分的唯一例外,我不清楚它是否是可选的(尽pipe它似乎没有添加用OpenSSL创build的证书)。
我可以打开证书导入证书存储(并可以成功导入到商店),所以我不认为任何文件/编码有什么特别的错误。
当我到达CertCreateCertificateContext的调用时,我的lp缓冲区如下所示:
-----BEGIN CERTIFICATE-----\nMIIBqzCCARQCCQDUJyWk0OxlRTANBgkqhkiG9w0BAQUFADAaMRgwFgYDVQQDDA9N\neSBTaWduaW5nIENlcnQwHhcNMTYwMTA1MjIzODU5WhcNMTcwMTA0MjIzODU5WjAa\nMRgwFgYDVQQDDA9NeSBTaWduaW5nIENlcnQwgZ8wDQ...
和字节= 639 – 这是文件大小。
我已经尝试添加逻辑去除证书注释,但以这种方式导入证书的示例并不表示应该有必要。
我试着将dwCertEncodingType设置为X509_ASN_ENCODING | PKCS_7_ASN_ENCODING和PKCS_7_ASN_ENCODING绝望(虽然我不相信我在这里使用PKCS#7编码…有点模糊)。
有没有人有什么build议,我可能做错了吗? 我很感激。
我想出了我的问题。 CertCreateCertificateContext期望二进制ASN.1数据,而不是我用openssl创建的PEM编码证书。 我通过使用Microsoft证书生成工具并测试该证书来解决这个问题:
C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin>makecert.exe -n "CN=Test Signing Cert" -b 0 1/06/2016 -e 01/06/2017 -len 1024 -r C:\Certificates\public_v2.crt Succeeded
在十六进制编辑器中查看文件,看起来就像ASN.1二进制数据。 接下来,我使用证书查看器中的“复制到文件”功能,当您双击证书将我的原始public.crt文件复制到DER编码的二进制X.509(.CER)文件并启动时,将启动我的程序工作(也就是说,CertCreateCertificateContext现在很开心)。
所以,如果有人遇到了同样的问题,这里有一个完整的解决方案,从一个文件导入一个PEM编码的证书到内存中用于Crypto API:
#include "stdafx.h" #include <windows.h> #include <wincrypt.h> #define LF 0x0A int _tmain(int argc, _TCHAR* argv[]) { char keyFile[] = "C:\\Certificates\\public.crt"; BYTE lp[65536]; SECURITY_ATTRIBUTES sa; HANDLE hKeyFile; DWORD bytes; PCCERT_CONTEXT certContext; BYTE *p; DWORD flags; sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = FALSE; hKeyFile = CreateFile(keyFile, GENERIC_READ, FILE_SHARE_READ, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hKeyFile) { if (ReadFile(hKeyFile, lp, GetFileSize(hKeyFile, NULL), &bytes, NULL) && bytes > 0) { p = lp + bytes; if (CryptStringToBinary((char *)lp, p - lp, CRYPT_STRING_BASE64_ANY, p, &bytes, NULL, &flags) && bytes > 0) { certContext = CertCreateCertificateContext(X509_ASN_ENCODING, p, bytes); if (certContext) { printf("yay!"); CertFreeCertificateContext(certContext); } else { printf("Could not convert certificate to internal form\n"); } } else { printf("Failed to convert from PEM"); } } else { printf("Failed to read key file: %s\n", keyFile); } } else { printf("Failed to open key file: %s\n", keyFile); } CloseHandle(hKeyFile); return 0; }
注意:
因为我很懒,我解码PEM编码为二进制在我用来加载文件相同的BYTE数组 – 这个简单的测试,这是适宜的,但如果你想实现这样的事情是真的,我不会推荐它