微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何将 PEM 格式的公钥转换为 CNG 密钥 blob?

如何解决如何将 PEM 格式的公钥转换为 CNG 密钥 blob?

我需要使用 Windows 的 Cryptography API: Next Generation 验证消息的签名。我有消息、其签名和 PEM 格式的公钥,以及展示所需行为的原型实现。

我需要修复代码中的一个问题:将公钥转换为 CNG 使用的密钥 blob。

目前,我正在解码公钥

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETHfi8foQF4UtSNVxSFxeu7W+gMxd
SGElhdo7825SD3Lyb+Sqh4G6Kra0ro1BdrM6Qx+hsUx4Qwdby7QY0pzxyA==
-----END PUBLIC KEY-----

使用 CryptStringToBinaryA,产生以下二进制数据:

30 59 30 13 06 07 2A 86  48 CE 3D 02 01 06 08 2A
86 48 CE 3D 03 01 07 03  42 00 04 4C 77 E2 F1 FA
10 17 85 2D 48 D5 71 48  5C 5E BB B5 BE 80 CC 5D
48 61 25 85 DA 3B F3 6E  52 0F 72 F2 6F E4 AA 87
81 BA 2A B6 B4 AE 8D 41  76 B3 3A 43 1F A1 B1 4C
78 43 07 5B CB B4 18 D2  9C F1 C8

一切看起来都很好。据推测,这是使用 ASN.1 的 DER 格式。它的大小为 91 个字节,指定椭圆曲线加密 (ECDSA prime256v1),其中 [x][y] 条目从偏移量 27 开始(每个条目总共 32 个字节)。

因为我需要一个密钥 blob 来传递到 BCryptImportKeyPair,特别是一个 BCRYPT_ECCKEY_BLOB

typedef struct _BCRYPT_ECCKEY_BLOB
{
    ULONG   dwMagic;
    ULONG   cbKey;
} BCRYPT_ECCKEY_BLOB,*PBCRYPT_ECCKEY_BLOB;

我的原型通过提供 BCRYPT_ECDSA_PUBLIC_P256_MAGIC 和长度为 32,然后是来自上述公钥的相应 64 字节来创建一个临时的。

现在可以了,但我显然想要实现更强大的功能。密钥似乎对填充适当的 CNG 密钥 blob 所需的所有数据进行编码,但我找不到任何可以帮助我解析此信息的 API。

CNG(或 Wincrypt)是否提供任何允许我构建 CNG 密钥 blob 或以其他方式从 PEM 密钥中获取 BCRYPT_KEY_HANDLE功能?还是我要实现自己的 ASN.1 解析器?

解决方法

将 PEM 公钥转换为 CNG - 接下来是通用步骤:

  1. CryptStringToBinaryA 用于将字符串转换为二进制
  2. CryptDecodeObjectExX509_PUBLIC_KEY_INFO - 转换二进制 CERT_PUBLIC_KEY_INFO
  3. CryptImportPublicKeyInfoEx2 - 将 CERT_PUBLIC_KEY_INFO 导入 CNG

代码示例

inline ULONG BOOL_TO_ERROR(BOOL f)
{
    return f ? NOERROR : GetLastError();
}

ULONG PemToCNG(_In_ PCSTR pszString,_Out_ BCRYPT_KEY_HANDLE* phKey)
{
    PBYTE pb = 0;
    ULONG cb = 0;

    ULONG dwError;

    while (NOERROR == (dwError = BOOL_TO_ERROR(
        CryptStringToBinaryA(pszString,CRYPT_STRING_BASE64_ANY,pb,&cb,0))))
    {
        if (pb)
        {
            PCERT_PUBLIC_KEY_INFO PublicKeyInfo;

            if (NOERROR == (dwError = BOOL_TO_ERROR(CryptDecodeObjectEx(
                X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,X509_PUBLIC_KEY_INFO,cb,CRYPT_DECODE_ALLOC_FLAG|
                CRYPT_DECODE_NOCOPY_FLAG|
                CRYPT_DECODE_SHARE_OID_STRING_FLAG,&PublicKeyInfo,&cb))))
            {
                dwError = BOOL_TO_ERROR(CryptImportPublicKeyInfoEx2(
                    X509_ASN_ENCODING,PublicKeyInfo,phKey));

                LocalFree(PublicKeyInfo);
            }
            break;
        }

        if (!(pb = (PBYTE)LocalAlloc(0,cb)))
        {
            dwError = GetLastError();
            break;
        }
    }

    if (pb)
    {
        LocalFree(pb);
    }

    return dwError;
}

void TestPem()
{
    static const char Pem[] = 
        "-----BEGIN PUBLIC KEY-----\r\n"
        "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETHfi8foQF4UtSNVxSFxeu7W+gMxd\r\n"
        "SGElhdo7825SD3Lyb+Sqh4G6Kra0ro1BdrM6Qx+hsUx4Qwdby7QY0pzxyA==\r\n"
        "-----END PUBLIC KEY-----";

    BCRYPT_KEY_HANDLE hKey;
    if (NOERROR == PemToCNG(Pem,&hKey))
    {
        PBYTE pb = 0;
        ULONG cb = 0;
        while (0 <= BCryptExportKey(hKey,BCRYPT_PUBLIC_KEY_BLOB,0))
        {
            if (pb)
            {
                PSTR psz = 0;
                ULONG cch = 0;
                while (CryptBinaryToStringA(pb,CRYPT_STRING_HEXASCII,psz,&cch))
                {
                    if (psz)
                    {
                        DbgPrint(psz);
                        break;
                    }

                    psz = (PSTR)alloca(cch * sizeof(char));
                }
                break;
            }

            pb = (PBYTE)alloca(cb);
        }

        BCryptDestroyKey(hKey);
    }

}

输出是

45 43 53 31 20 00 00 00  4c 77 e2 f1 fa 10 17 85   ECS1 ...Lw......
2d 48 d5 71 48 5c 5e bb  b5 be 80 cc 5d 48 61 25   -H.qH\^.....]Ha
85 da 3b f3 6e 52 0f 72  f2 6f e4 aa 87 81 ba 2a   ..;.nR.r.o.....*
b6 b4 ae 8d 41 76 b3 3a  43 1f a1 b1 4c 78 43 07   ....Av.:C...LxC.
5b cb b4 18 d2 9c f1 c8                            [.......

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。