【问题标题】:CertCreateCertificateContext returns CRYPT_E_ASN1_BADTAG / 8009310bCertCreateCertificateContext 返回 CRYPT_E_ASN1_BADTAG / 8009310b
【发布时间】:2016-04-09 23:02:48
【问题描述】:

我知道这是一篇与其他人非常相似的帖子(例如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

使用以下配置文件:

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
   ...
}

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

我可以打开证书以导入证书存储(并且可以成功导入存储),所以我认为文件/编码没有任何特别问题。

当我调用 CertCreateCertificateContext 时,我的 lp 缓冲区如下所示:

-----BEGIN CERTIFICATE-----\nMIIBqzCCARQCCQDUJyWk0OxlRTANBgkqhkiG9w0BAQUFADAaMRgwFgYDVQQDDA9N\neSBTaWduaW5nIENlcnQwHhcNMTYwMTA1MjIzODU5WhcNMTcwMTA0MjIzODU5WjAa\nMRgwFgYDVQQDDA9NeSBTaWduaW5nIENlcnQwgZ8wDQ...

和 bytes = 639 -- 这是文件大小。

我已尝试添加逻辑以删除证书 cmets,但 examples 以这种方式导入证书并不表示这应该是必要的。

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

有人对我在这里可能做错了什么有任何建议吗?我很感激。

【问题讨论】:

标签: windows winapi encryption certificate cryptoapi


【解决方案1】:

我发现了我的问题。 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 数组中——对于这个简单的测试,它是权宜之计,但如果你想实现这种真实的东西,我不会推荐它

【讨论】:

    猜你喜欢
    • 2010-12-15
    • 1970-01-01
    • 2020-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-23
    • 2018-09-15
    相关资源
    最近更新 更多