CertCreateCertificateContext

CertCreateCertificateContext

我意识到这与其他帖子(例如this one)非常相似,但是这些帖子中缺少一些细节,这可能对我的情况很重要。

首先,这是我的简化程序:

#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;
}

为了创建证书,我对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编码转换为十六进制,可以识别证书的各个部分:
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 specs(正如我期望的那样):
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
   ...
}

除了版本部分的唯一异常(exception),我不清楚它是否是可选的(尽管似乎从未与我使用OpenSSL创建的证书一起添加)。

我可以打开证书以导入到证书存储中(并且可以成功导入到存储中),因此我认为文件/编码没有什么特别的问题。

当我到达对CertCreateCertificateContext的调用时,我的lp缓冲区如下所示:
-----BEGIN CERTIFICATE-----\nMIIBqzCCARQCCQDUJyWk0OxlRTANBgkqhkiG9w0BAQUFADAaMRgwFgYDVQQDDA9N\neSBTaWduaW5nIENlcnQwHhcNMTYwMTA1MjIzODU5WhcNMTcwMTA0MjIzODU5WjAa\nMRgwFgYDVQQDDA9NeSBTaWduaW5nIENlcnQwgZ8wDQ...

和字节= 639-这是文件大小。

我尝试添加逻辑以去除证书注释,但是以这种方式导入证书的examples并不表明这是必要的。

我尝试将dwCertEncodingType设置为X509_ASN_ENCODING | PKCS_7_ASN_ENCODING和PKCS_7_ASN_ENCODING出于绝望(尽管我不相信我在这里使用PKCS#7编码...对此有点模糊)。

有人对我在这里可能做错的事情有任何建议吗?我很感激。

最佳答案

我知道了我的问题。 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数组中的二进制文件-对于这个简单的测试,这很方便,但是如果您希望真正实现这种事情,我不推荐

09-28 04:28