windows - CertCreateCertificateContext 返回 CRYPT_E_ASN1_BADTAG/8009310b

标签 windows winapi encryption certificate cryptoapi

我意识到这是一篇与其他帖子(例如 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
   ...
}

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

我可以打开证书导入证书存储(并且可以成功导入存储),所以我不认为文件/编码有什么特别的错误。

当我到达对 CertCreateCertificateContext 的调用时,我的 lp 缓冲区看起来像:

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

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

关于windows - CertCreateCertificateContext 返回 CRYPT_E_ASN1_BADTAG/8009310b,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34623247/

相关文章:

windows - FOR/F 命令循环中的 ERRORLEVEL 返回意外结果

Windows命令提示符记录到文件

windows - Git pull 错误 : unable to create file (Invalid argument)

c++ - c++中windows-thread异常输出

c# - timeBeginPeriod 不适用于 Intel Comet Lake CPU (i5 10400H)

c++ - midiOutShortMsg 和 midiOutSetVolume 以立体声输出

python - 在Python中执行带空格的Windows路径

java - jasypt PBKDF2 实现

python - 有时在加密数据时我的代码返回带有奇怪符号的特定结果

针对多个未知收件人的 Java 文本加密