无法通过 CNG API 将 NodeJS 生成的公钥加载到 Windows 中

问题描述 投票:0回答:2

我使用

crypto
库在 NodeJS 中生成 RSA 公钥/私钥对:

crypto.generateKeyPair(
    "rsa",
    {
        modulusLength: 1024,
        publicKeyEncoding: {
            type: "pkcs1",
            format: "pem",
        },
        privateKeyEncoding: {
            type: "pkcs1",
            format: "pem",
        },
    },
    (err, publicKey, privateKey) => {
        fs.writeFileSync("/home/dev/priv.pem", privateKey);
        fs.writeFileSync("/home/dev/pub.pem", publicKey);
    } );

现在,当我想使用 Windows Cryptographic CNG API 加载公钥时,它失败了。

CryptDecodeObjectEx
返回 0x8009310b(ASN1 错误标记值错误)。我使用 this 链接来加载公钥。你知道我做错了什么吗?我应该以特定方式生成密钥吗?

javascript c++ node.js windows cryptography
2个回答
1
投票

请将“pkcs1”字符串替换为“spki”。该代码不需要 RSA 密钥,它需要一个SubjectPublicKeyInfo 编码的密钥。

SPKI是在X.509标准中定义的,它允许ASN.1编码来指示任何类型的密钥。例如,它还可以包含各种类型的基于椭圆曲线的密钥。它在内部包含 PKCS#1 编码密钥,但在本例中,密钥类型的指示符(在本例中为 RSA 密钥)采用二进制编码而不是文本编码。

备注:

  • 您不应使用“教科书上的 RSA”,而应始终使用某种安全填充,例如 PKCS#1 填充或 OAEP 填充。
    BCRYPT_PAD_NONE
    仅在您定义自己的方案或实施另一个现有方案时才有用,这可能是最好避免的。如果您想加密更大的消息,请考虑混合加密(例如加密 AES 密钥)。
  • 如今 1024 位密钥被认为太小;请使用至少 2048 位的密钥。
  • 您可能想查看 CMS 甚至 PGP 等容器格式,而不是直接执行 RSA 加密。

1
投票

不同的 PEM 块(在

-----BEGIN X-----
...
-----END X-----
之间)具有不同的格式。因此需要在调用
CryptDecodeObjectEx
时使用不同的常量来代替 lpszStructType。常见 - 首先我们需要将 base64 转换为二进制(使用
CryptStringToBinaryA
)。比使用基于
CryptDecodeObjectEx
lpszStructType 来调用
X

template <typename T> 
T HR(HRESULT& hr, T t)
{
    hr = t ? S_OK : GetLastError();
    return t;
}

NTSTATUS ImportRsaKey(_Out_ BCRYPT_KEY_HANDLE* phKey, _In_ PCWSTR pszBlobType, _In_reads_(cb) BYTE* pb, _In_ ULONG cb)
{
    BCRYPT_ALG_HANDLE hAlgorithm;
    NTSTATUS status = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_RSA_ALGORITHM, 0, 0);
    if (0 <= status)
    {
        status = BCryptImportKeyPair(hAlgorithm, 0, pszBlobType, phKey, pb, cb, 0);
        BCryptCloseAlgorithmProvider(hAlgorithm, 0);
    }

    return status;
}

NTSTATUS ImportEccKey(_Out_ BCRYPT_KEY_HANDLE* phKey, _In_ PCRYPT_ECC_PRIVATE_KEY_INFO p, _In_ UCHAR IsEncrypt)
{
    PCCRYPT_OID_INFO pOidInfo = CryptFindOIDInfo(IsEncrypt 
        ? CRYPT_OID_INFO_OID_KEY|CRYPT_OID_INFO_PUBKEY_ENCRYPT_KEY_FLAG 
        : CRYPT_OID_INFO_OID_KEY|CRYPT_OID_INFO_PUBKEY_SIGN_KEY_FLAG, 
        p->szCurveOid, 
        CRYPT_PUBKEY_ALG_OID_GROUP_ID | CRYPT_OID_PREFER_CNG_ALGID_FLAG);

    if (!pOidInfo)
    {
        return STATUS_NOT_FOUND;
    }

    if (pOidInfo->cbSize < sizeof(CRYPT_OID_INFO) ||
        !pOidInfo->pwszCNGAlgid ||
        !pOidInfo->ExtraInfo.pbData ||
        pOidInfo->ExtraInfo.cbData < 3 * sizeof(ULONG))
    {
        return STATUS_INTERNAL_ERROR;
    }

    ULONG cbKey = p->PrivateKey.cbData;

    if (1 + (cbKey << 1) != p->PublicKey.cbData)
    {
        return STATUS_INVALID_PARAMETER;
    }

    ULONG cb = sizeof(BCRYPT_ECCKEY_BLOB) + cbKey * 3;
    PBCRYPT_ECCKEY_BLOB pecc = (PBCRYPT_ECCKEY_BLOB)alloca(cb);
    pecc->cbKey = cbKey;
    pecc->dwMagic = ((ULONG*)pOidInfo->ExtraInfo.pbData)[1] + 0x01000000;
    PBYTE pb = (PBYTE)(pecc+1);

    memcpy(pb, p->PublicKey.pbData + 1, cbKey << 1);
    memcpy(pb + (cbKey << 1), p->PrivateKey.pbData, cbKey);

    BCRYPT_ALG_HANDLE hAlgorithm;
    NTSTATUS status = BCryptOpenAlgorithmProvider(&hAlgorithm, pOidInfo->pwszCNGAlgid, 0, 0);
    if (0 <= status)
    {
        status = BCryptImportKeyPair(hAlgorithm, 0, BCRYPT_ECCPRIVATE_BLOB, phKey, (PUCHAR)pecc, cb, 0);
        BCryptCloseAlgorithmProvider(hAlgorithm, 0);
    }

    return status;
}

UCHAR IsEncryptKey(PCRYPT_ATTRIBUTES pAttributes)
{
    if (pAttributes)
    {
        if (DWORD cAttr = pAttributes->cAttr)
        {
            UCHAR Usage = 0;

            PCRYPT_ATTRIBUTE rgAttr = pAttributes->rgAttr;

            do 
            {
                if (!strcmp(szOID_KEY_USAGE, rgAttr->pszObjId))
                {
                    if (DWORD cValue = rgAttr->cValue)
                    {
                        PCRYPT_ATTR_BLOB rgValue = rgAttr->rgValue;
                        do 
                        {
                            ULONG cb;
                            PCRYPT_BIT_BLOB KeyUsage;

                            if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_KEY_USAGE, 
                                rgValue->pbData, rgValue->cbData, 
                                CRYPT_DECODE_NOCOPY_FLAG|CRYPT_DECODE_ALLOC_FLAG, 0, &KeyUsage, &cb))
                            {
                                Usage |= (*KeyUsage->pbData & 
                                    (CERT_KEY_ENCIPHERMENT_KEY_USAGE|CERT_DATA_ENCIPHERMENT_KEY_USAGE|CERT_ENCIPHER_ONLY_KEY_USAGE));

                                if (KeyUsage->cbData > 1)
                                {
                                    Usage |= (KeyUsage->pbData[1] & CERT_DECIPHER_ONLY_KEY_USAGE);
                                }
                                LocalFree(KeyUsage);
                            }

                        } while (rgValue++, --cValue);
                    }
                }
            } while (rgAttr++, --cAttr);

            return Usage;
        }
    }

    return 0;
}

HRESULT PkcsImportPlainTextKey(_Out_ BCRYPT_KEY_HANDLE* phKey, _In_reads_(cb) BYTE* pb, _In_ ULONG cb)
{
    PCRYPT_PRIVATE_KEY_INFO PrivateKeyInfo;

    HRESULT hr;

    if (HR(hr, CryptDecodeObjectEx(
        X509_ASN_ENCODING, PKCS_PRIVATE_KEY_INFO, pb, cb, 
        CRYPT_DECODE_ALLOC_FLAG|CRYPT_DECODE_NOCOPY_FLAG|CRYPT_DECODE_SHARE_OID_STRING_FLAG, 
        0, &PrivateKeyInfo, &cb)))
    {
        PSTR pszObjId = PrivateKeyInfo->Algorithm.pszObjId;

        PCSTR lpszStructType = 0;

        if (!strcmp(pszObjId, szOID_RSA_RSA))
        {
            lpszStructType = CNG_RSA_PRIVATE_KEY_BLOB;
        }
        else if (!strcmp(pszObjId, szOID_ECC_PUBLIC_KEY))
        {
            lpszStructType = X509_ECC_PRIVATE_KEY;
        }
        //else if (!strcmp(pszObjId, szOID_X957_DSA) || !strcmp(pszObjId, szOID_OIWSEC_dsa))
        //{
        //  // _PkcsImportPlainTextKeyDsa
        //  // KspImportDsaPrivateKey DSS2 0x32535344
        //  lpszStructType = X509_DSS_PARAMETERS;
        //}
        else
        {
            hr = NTE_BAD_ALGID;
        }

        if (lpszStructType)
        {
            if (HR(hr, CryptDecodeObjectEx(X509_ASN_ENCODING, lpszStructType,
                PrivateKeyInfo->PrivateKey.pbData, PrivateKeyInfo->PrivateKey.cbData, 
                CRYPT_DECODE_ALLOC_FLAG|CRYPT_DECODE_NOCOPY_FLAG|CRYPT_DECODE_SHARE_OID_STRING_FLAG, 0, &pb, &cb)))
            {
                switch ((ULONG_PTR)lpszStructType)
                {
                case (ULONG_PTR)CNG_RSA_PRIVATE_KEY_BLOB:
                    hr = ImportRsaKey(phKey, BCRYPT_RSAPRIVATE_BLOB, pb, cb);
                    break;

                case (ULONG_PTR)X509_ECC_PRIVATE_KEY:

                    if (!reinterpret_cast<PCRYPT_ECC_PRIVATE_KEY_INFO>(pb)->szCurveOid)
                    {
                        union {
                            PSTR szCurveOid;
                            UCHAR buf[0x20 + sizeof(void*)];
                        };
                        ULONG cbCurveOid = sizeof(buf);

                        if (!HR(hr, CryptDecodeObjectEx(X509_ASN_ENCODING, X509_OBJECT_IDENTIFIER, 
                            PrivateKeyInfo->Algorithm.Parameters.pbData, 
                            PrivateKeyInfo->Algorithm.Parameters.cbData,
                            CRYPT_DECODE_NOCOPY_FLAG, 0, &szCurveOid, &cbCurveOid)))
                        {
                            break;
                        }

                        reinterpret_cast<PCRYPT_ECC_PRIVATE_KEY_INFO>(pb)->szCurveOid = szCurveOid;
                    }

                    hr = ImportEccKey(phKey, 
                        reinterpret_cast<PCRYPT_ECC_PRIVATE_KEY_INFO>(pb), 
                        IsEncryptKey(PrivateKeyInfo->pAttributes));

                    break;
                }

                LocalFree(pb);
            }
        }

        LocalFree(PrivateKeyInfo);
    }

    return hr;
}

struct CryptCNGMap 
{
    struct ENCODE_DECODE_PARA {
        PFN_CRYPT_ALLOC         pfnAlloc;           // OPTIONAL
        PFN_CRYPT_FREE          pfnFree;            // OPTIONAL
    };

    struct SECRET_APPEND
    {
        ULONG IterationCount;
        ULONG cb;
        UCHAR buf[];
    };

    ULONG version;

    NTSTATUS (WINAPI * InitHash)(
        _Out_ BCRYPT_ALG_HANDLE* phAlgorithm, 
        _Out_ ULONG* pcbObjectLength,
        _In_ ULONG dwFlags);

    NTSTATUS (WINAPI *InitEncrypt)(
        _Out_ BCRYPT_ALG_HANDLE* phAlgorithm, 
        _Out_ ULONG* pcbObjectLength, 
        _In_ BYTE* pbParams, 
        _In_ ULONG cbParams
        );

    NTSTATUS (WINAPI *InitDecrypt)(
        _Out_ BCRYPT_ALG_HANDLE* phAlgorithm, 
        _Out_ ULONG* pcbObjectLength, 
        _In_ BYTE* pbParams, 
        _In_ ULONG cbParams
        );

    NTSTATUS (WINAPI *InitEncryptKey)(
        _In_ BCRYPT_ALG_HANDLE hAlgorithm,
        _Out_ BCRYPT_KEY_HANDLE* phKey,
        _Out_ BYTE* pbKeyObject, 
        _In_ ULONG cbKeyObject,
        _In_ const BYTE* pbSecret,
        _In_ ULONG cbSecret,
        _Out_ BYTE* pbParams,
        _Out_ ULONG cbParams
        );

    NTSTATUS (WINAPI *InitDecryptKey)(
        _In_ BCRYPT_ALG_HANDLE hAlgorithm,
        _Out_ BCRYPT_KEY_HANDLE* phKey,
        _Out_ BYTE* pbKeyObject, 
        _In_ ULONG cbKeyObject,
        _In_ const BYTE* pbSecret,
        _In_ ULONG cbSecret,
        _Out_ BYTE* pbParams,
        _Out_ ULONG cbParams
        );

    NTSTATUS (WINAPI *PasswordDeriveKey)(
        _In_ CryptCNGMap* map, 
        _In_ UCHAR SECRET_PREPEND,
        _In_ PCWSTR pszPassword,
        _In_ ULONG cbPassword, // wcslen(pszPassword)*sizeof(WCHAR)
        _In_opt_ SECRET_APPEND* p,
        _In_opt_ ULONG cb,
        _Out_ PBYTE pbOutput,
        _Out_ ULONG cbOutput
        );

    NTSTATUS (WINAPI *ParamsEncode)(
        _In_ const BYTE* pb, 
        _In_ ULONG cb, 
        _In_ const ENCODE_DECODE_PARA* pEncodePara,
        _Out_ BYTE** ppbEncoded,
        _Out_ ULONG* pcbEncoded
        );

    NTSTATUS (WINAPI *ParamsDecode)(
        _In_ const BYTE* pbEncoded, 
        _In_ ULONG cbEncoded, 
        _In_ const ENCODE_DECODE_PARA* pDecodePara,
        _Out_ BYTE** ppb,
        _Out_ ULONG* pcb
        );
};

void* WINAPI PkiAlloc(_In_ size_t cbSize)
{
    return LocalAlloc(LMEM_FIXED, cbSize);
}

void WINAPI PkiFree(void* pv)
{
    LocalFree(pv);
}

HRESULT GetLastErrorEx(ULONG dwError /*= GetLastError()*/)
{
    NTSTATUS status = RtlGetLastNtStatus();
    return RtlNtStatusToDosErrorNoTeb(status) == dwError ? HRESULT_FROM_NT(status) : HRESULT_FROM_WIN32(dwError);
}

HRESULT DecryptPrivateKey(_Inout_ PCRYPT_ENCRYPTED_PRIVATE_KEY_INFO pepki, _In_ PCWSTR pszPassword)
{
    HCRYPTOIDFUNCADDR hFuncAddr;

    if (HCRYPTOIDFUNCSET hFuncSet = CryptInitOIDFunctionSet("CryptCNGPKCS12GetMap", 0))
    {
        union {
            PVOID pvFuncAddr;
            CryptCNGMap* (WINAPI *GetPKCS12Map)();
        };

        if (CryptGetOIDFunctionAddress(hFuncSet, X509_ASN_ENCODING, 
            pepki->EncryptionAlgorithm.pszObjId, CRYPT_GET_INSTALLED_OID_FUNC_FLAG, &pvFuncAddr, &hFuncAddr))
        {
            CryptCNGMap* map = GetPKCS12Map();

            static const CryptCNGMap::ENCODE_DECODE_PARA cdp = { PkiAlloc, PkiFree };

            NTSTATUS status;

            if (0 <= (status = map->ParamsDecode(
                pepki->EncryptionAlgorithm.Parameters.pbData,
                pepki->EncryptionAlgorithm.Parameters.cbData, 
                &cdp, 
                &pepki->EncryptionAlgorithm.Parameters.pbData,
                &pepki->EncryptionAlgorithm.Parameters.cbData)))
            {
                ULONG cb;
                BCRYPT_ALG_HANDLE hAlgorithm;

                if (0 <= (status = map->InitDecrypt(&hAlgorithm, &cb, 
                    pepki->EncryptionAlgorithm.Parameters.pbData,
                    pepki->EncryptionAlgorithm.Parameters.cbData)))
                {
                    BCRYPT_KEY_HANDLE hKey;

                    PBYTE pbSecret = (PBYTE)pszPassword;
                    ULONG cbSecret = (ULONG)wcslen(pszPassword);

                    struct CRYPT_PKCS12_PBES2_PARAMS
                    {
                        /*00*/BOOL bUTF8;
                        /*04*/CHAR pszObjId[0x20];//szOID_PKCS_5_PBKDF2 "1.2.840.113549.1.5.12"
                        /*24*/ULONG cbSalt;
                        /*28*/UCHAR pbSalt[0x20];
                        /*48*/ULONG cIterations;
                        /*4c*/ULONG pad;
                        /*50*/CHAR pszHMACAlgorithm[0x20];//PKCS12_PBKDF2_ID_HMAC_SHAxxx -> BCRYPT_HMAC_SHAxxx_ALG_HANDLE
                        /*70*/CHAR pszKeyAlgorithm[0x20];//szOID_NIST_AESxxx_CBC
                        /*90*/ULONG cbIV;
                        /*94*/UCHAR pbIV[0x20];
                        /*b4*/
                    };

                    PBYTE pbIV = 0;
                    ULONG cbIV = 0;
                    BOOL bUTF8 = FALSE;

                    CRYPT_PKCS12_PBES2_PARAMS* py = reinterpret_cast<CRYPT_PKCS12_PBES2_PARAMS*>(
                        pepki->EncryptionAlgorithm.Parameters.pbData);

                    if (sizeof(CRYPT_PKCS12_PBES2_PARAMS) == pepki->EncryptionAlgorithm.Parameters.cbData &&
                        !strcmp(szOID_PKCS_5_PBKDF2, py->pszObjId))
                    {
                        pbIV = py->pbIV;
                        cbIV = py->cbIV;
                        if (cbIV > sizeof(py->pbIV))
                        {
                            status = STATUS_BAD_DATA;
                            goto __0;
                        }

                        if (bUTF8 = py->bUTF8)
                        {
                            PSTR psz = 0;
                            ULONG cch = 0;
                            while(cch = WideCharToMultiByte(CP_UTF8, 0, pszPassword, cbSecret, psz, cch, 0, 0))
                            {
                                if (psz)
                                {
                                    pbSecret = (PBYTE)psz;
                                    cbSecret = cch;
                                    break;
                                }

                                psz = (PSTR)alloca(cch);
                            }
                        }
                    }

                    if (!bUTF8)
                    {
                        ++cbSecret *= sizeof(WCHAR);
                    }

                    status = map->InitDecryptKey(hAlgorithm, &hKey, 
                        (PBYTE)alloca(cb), cb, pbSecret, cbSecret,
                        pepki->EncryptionAlgorithm.Parameters.pbData,
                        pepki->EncryptionAlgorithm.Parameters.cbData 
                        );

                    BCryptCloseAlgorithmProvider(hAlgorithm, 0);

                    if (0 <= status)
                    {
                        status = BCryptDecrypt(hKey, 
                            pepki->EncryptedPrivateKey.pbData,
                            pepki->EncryptedPrivateKey.cbData, 
                            0, pbIV, cbIV, 
                            pepki->EncryptedPrivateKey.pbData,
                            pepki->EncryptedPrivateKey.cbData,
                            &pepki->EncryptedPrivateKey.cbData, 0);

                        BCryptDestroyKey(hKey);
                    }
                }
__0:
                LocalFree(pepki->EncryptionAlgorithm.Parameters.pbData);
            }

            CryptFreeOIDFunctionAddress(hFuncAddr, 0);

            return status ? HRESULT_FROM_NT(status) : STATUS_SUCCESS;
        }
    }

    return GetLastErrorEx();
}

HRESULT PkcsImportEncodedKey(_Out_ BCRYPT_KEY_HANDLE* phKey, _In_reads_(cb) BYTE* pb, _In_ ULONG cb, _In_ PCWSTR pszPassword)
{
    HRESULT hr;
    PCRYPT_ENCRYPTED_PRIVATE_KEY_INFO pepki;
    ULONG s;
    if (HR(hr, CryptDecodeObjectEx(
        X509_ASN_ENCODING, PKCS_ENCRYPTED_PRIVATE_KEY_INFO, 
        pb, cb, CRYPT_DECODE_ALLOC_FLAG|CRYPT_DECODE_SHARE_OID_STRING_FLAG, 0, &pepki, &s)))
    {
        if (S_OK == (hr = DecryptPrivateKey(pepki, pszPassword)))
        {
            hr = PkcsImportPlainTextKey(phKey,
                pepki->EncryptedPrivateKey.pbData,
                pepki->EncryptedPrivateKey.cbData);
        }

        LocalFree(pepki);
    }

    return hr;
}

HRESULT PkcsImportKey(_Out_ NCRYPT_KEY_HANDLE* phKey, _In_reads_(cb) BYTE* pb, _In_ ULONG cb, _In_opt_ PCWSTR pszPassword /*= 0*/)
{
    NCRYPT_PROV_HANDLE hProvider;
    SECURITY_STATUS status = NCryptOpenStorageProvider(&hProvider, MS_KEY_STORAGE_PROVIDER, 0);

    if (NOERROR == status)
    {
        NCryptBufferDesc *pParameterList = 0;
        BCryptBuffer buf;
        NCryptBufferDesc ParameterList { NCRYPTBUFFER_VERSION, 1, &buf };

        if (pszPassword)
        {
            buf.BufferType = NCRYPTBUFFER_PKCS_SECRET;
            buf.cbBuffer = (1 + (ULONG)wcslen(pszPassword)) * sizeof(WCHAR);
            buf.pvBuffer = const_cast<PWSTR>(pszPassword);

            pParameterList = &ParameterList;
        }

        NCRYPT_KEY_HANDLE hKey;

        status = NCryptImportKey(hProvider, 0, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, 
            pParameterList, &hKey, pb, cb, NCRYPT_DO_NOT_FINALIZE_FLAG);

        NCryptFreeObject(hProvider);

        if (NOERROR == status)
        {
            static const ULONG flags = NCRYPT_ALLOW_EXPORT_FLAG|NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;

            if (NOERROR == (status = NCryptSetProperty(hKey, NCRYPT_EXPORT_POLICY_PROPERTY, (PBYTE)&flags, sizeof(flags), 0)) &&
                NOERROR == (status = NCryptFinalizeKey(hKey, NCRYPT_SILENT_FLAG)))
            {
                *phKey = hKey;

                return NOERROR;
            }

            NCryptFreeObject(hKey);
        }
    }

    return status;
}

HRESULT NKeyToBKey(_Out_ BCRYPT_KEY_HANDLE* phKey, _In_ NCRYPT_KEY_HANDLE hKey)
{
    WCHAR szAlgId[0x40];
    ULONG cb = sizeof(szAlgId);

    HRESULT status = NCryptGetProperty(hKey, NCRYPT_ALGORITHM_PROPERTY, (PBYTE)szAlgId, sizeof(szAlgId), &cb, 0);

    if (NOERROR == status)
    {
        cb = 0;
        PBYTE pb = 0;

        while(NOERROR == (status = NCryptExportKey(hKey, 0, BCRYPT_PRIVATE_KEY_BLOB, 0, pb, cb, &cb, 0)))
        {
            if (pb)
            {
                BCRYPT_ALG_HANDLE hAlgorithm;
                if (0 <= (status = BCryptOpenAlgorithmProvider(&hAlgorithm, szAlgId, 0, 0)))
                {
                    status = BCryptImportKeyPair(hAlgorithm, 0, BCRYPT_PRIVATE_KEY_BLOB, phKey, pb, cb, 0);
                    BCryptCloseAlgorithmProvider(hAlgorithm, 0);
                }

                break;
            }

            pb = (PBYTE)alloca(cb);
        }
    }

    return status;
}

HRESULT PkcsImportKey(_Out_ BCRYPT_KEY_HANDLE* phKey, _In_reads_(cb) BYTE* pb, _In_ ULONG cb, _In_ PCWSTR pszPassword)
{
    HRESULT hr;
    NCRYPT_KEY_HANDLE hKey;
    if (S_OK == (hr = PkcsImportKey(&hKey, pb, cb, pszPassword)))
    {
        hr = NKeyToBKey(phKey, hKey);
        NCryptFreeObject(hKey);
    }

    return hr;
}

/////////////////////////////////////////////////////////////

class __declspec(novtable) Pem
{
    virtual HRESULT process(_In_reads_(cb) BYTE* pb, _In_ ULONG cb) = 0;

public:
    virtual ~Pem() = default;

    HRESULT import(_In_reads_(cch) PCSTR psz, _In_ ULONG cch)
    {
        HRESULT hr;

        PBYTE pb = 0;
        ULONG cb = 0;

        while (HR(hr, CryptStringToBinaryA(psz, cch, CRYPT_STRING_BASE64, pb, &cb, 0, 0)))
        {
            if (pb)
            {
                hr = process(pb, cb);
                break;
            }

            if (!(pb = new UCHAR[cb]))
            {
                hr = E_OUTOFMEMORY;
                break;
            }
        }

        if (pb) delete [] pb;

        return hr;
    }

    void* operator new(size_t s, void* pv, ULONG cb)
    {
        return cb < s ? 0 : pv;
    }

    void operator delete (void* /*pv*/)
    {
    }
};

class PemCert : public Pem
{
    HCERTSTORE _M_hCertStore;

    virtual HRESULT process(_In_reads_(cb) BYTE* pb, _In_ ULONG cb)
    {
        HRESULT hr;
        HR(hr, CertAddEncodedCertificateToStore(_M_hCertStore, X509_ASN_ENCODING, pb, cb, CERT_STORE_ADD_NEW, 0));
        return hr;
    }

public:

    PemCert(HCERTSTORE hCertStore) : _M_hCertStore(hCertStore)
    {
    }
};

class __declspec(novtable) PemKey : public Pem
{
protected:

    BCRYPT_KEY_HANDLE _M_hKey = 0;

    virtual ~PemKey()
    {
        if (_M_hKey)
        {
            BCryptDestroyKey(_M_hKey);
        }
    }
public:

    BCRYPT_KEY_HANDLE get()
    {
        BCRYPT_KEY_HANDLE hKey = _M_hKey;
        _M_hKey = 0;
        return hKey;
    }
};

struct __declspec(novtable) PemRsaKey : public PemKey
{
    virtual PCSTR GetStructType() = 0;

    virtual PCWSTR GetBlobType() = 0;

    virtual HRESULT process(_In_reads_(cb) BYTE* pb, _In_ ULONG cb)
    {
        HRESULT hr;
        if (HR(hr, CryptDecodeObjectEx(X509_ASN_ENCODING, GetStructType(), pb, cb, 
            CRYPT_DECODE_ALLOC_FLAG|CRYPT_DECODE_NOCOPY_FLAG|CRYPT_DECODE_SHARE_OID_STRING_FLAG, 0, &pb, &cb)))
        {
            hr = ImportRsaKey(&_M_hKey, GetBlobType(), pb, cb);

            LocalFree(pb);
        }

        return hr;
    }
};

struct PemRsaPublicKey : public PemRsaKey
{
    virtual PCSTR GetStructType() 
    {
        return CNG_RSA_PUBLIC_KEY_BLOB;
    }

    virtual PCWSTR GetBlobType()
    {
        return BCRYPT_RSAPUBLIC_BLOB;
    }
};

struct PemRsaPrivateKey : public PemRsaKey
{
    virtual PCSTR GetStructType() 
    {
        return CNG_RSA_PRIVATE_KEY_BLOB;
    }

    virtual PCWSTR GetBlobType()
    {
        return BCRYPT_RSAPRIVATE_BLOB;
    }
};

struct PemPublicKey : public PemKey
{
    virtual HRESULT process(_In_reads_(cb) BYTE* pb, _In_ ULONG cb)
    {
        HRESULT hr;
        PCERT_PUBLIC_KEY_INFO PublicKeyInfo;

        if (HR(hr, CryptDecodeObjectEx(X509_ASN_ENCODING, 
            X509_PUBLIC_KEY_INFO, pb, cb, 
            CRYPT_DECODE_ALLOC_FLAG|CRYPT_DECODE_NOCOPY_FLAG|CRYPT_DECODE_SHARE_OID_STRING_FLAG, 
            0, &PublicKeyInfo, &cb)))
        {
            HR(hr, CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, PublicKeyInfo, 0, 0, &_M_hKey));

            LocalFree(PublicKeyInfo);
        }

        return hr;
    }
};

struct PemPrivateKey : public PemKey
{
    virtual HRESULT process(_In_reads_(cb) BYTE* pb, _In_ ULONG cb)
    {
        return PkcsImportPlainTextKey2(&_M_hKey, pb, cb);
    }
};

struct PemEncryptedPrivateKey : public PemPrivateKey
{
    PCWSTR _M_pszPassword;

    PemEncryptedPrivateKey(PCWSTR pszPassword) : _M_pszPassword(pszPassword)
    {
    }

    virtual HRESULT process(_In_reads_(cb) BYTE* pb, _In_ ULONG cb)
    {
        return PkcsImportEncodedKey2(&_M_hKey, pb, cb, _M_pszPassword);
    }
};
/////////////////////////////////////////////////////////

extern const volatile UCHAR guz = 0;

PSTR __fastcall strnstr(SIZE_T n1, const void* str1, SIZE_T n2, const void* str2);

#define _strnstr(a, b, x) strnstr(RtlPointerToOffset(a, b), a, sizeof(x) - 1, x)

PCSTR IsTag(_In_ PCSTR buf, _In_ PCSTR end, _In_ PCSTR TAG, _In_ ULONG cb)
{
    return end - buf < cb || memcmp(buf, TAG, cb) ? 0 : buf + cb;
}

PCSTR IsBegin(_In_ PCSTR buf, _In_ PCSTR end)
{
    const static char BEGIN[] = "BEGIN";
    return IsTag(buf, end, BEGIN, sizeof(BEGIN) - 1);
}

PCSTR IsEnd(_In_ PCSTR buf, _In_ PCSTR end)
{
    const static char END[] = "END";
    return IsTag(buf, end, END, sizeof(END) - 1);
}

void FreeKeysI(_In_ BCRYPT_KEY_HANDLE* phKeys, _In_ ULONG nKeys)
{
    if (nKeys)
    {
        do 
        {
            BCryptDestroyKey(*phKeys++);
        } while (--nKeys);
    }
}

void FreeKeys(_In_ BCRYPT_KEY_HANDLE* phKeys, _In_ ULONG nKeys)
{
    FreeKeysI(phKeys, nKeys);
    LocalFree(phKeys);
}

HRESULT PEMImport(_Out_ HCERTSTORE* phStore,
                  _Out_ BCRYPT_KEY_HANDLE** pphKeys,
                  _Out_ ULONG* pnKeys,
                  _In_ PCSTR buf, 
                  _In_ PCSTR end,
                  _In_ PCWSTR pszPassword)
{
    const static char _____[] = "-----";
    const static char ENCRYPTED_PRIVATE_KEY[] = "ENCRYPTED PRIVATE KEY";
    const static char CERTIFICATE[] = "CERTIFICATE";

    enum { fInvalid ,fCert, fEncPrivKey, fPrivKey, fPubKey, fRsaPubKey, fRsaPrivKey } bt;

    *pphKeys = 0;
    *phStore = 0;
    *pnKeys = 0;

    HRESULT hr;
    PVOID stack = alloca(guz);
    BCRYPT_KEY_HANDLE* keys = (BCRYPT_KEY_HANDLE*)stack;
    ULONG nKeys = 0, nCerts = 0;

    if (HCERTSTORE hStore = HR(hr, CertOpenStore(sz_CERT_STORE_PROV_MEMORY, 0, 0, 0, 0)))
    {
        while (buf = _strnstr(buf, end, _____))
        {
            hr = HRESULT_FROM_NT(STATUS_INVALID_IMAGE_FORMAT);

            bt = fInvalid;

            if (!(buf = IsBegin(buf, end - sizeof(_____))) || *buf++ != ' ')
            {
                break;
            }

            PCSTR pcTag = buf;

            if (buf = IsTag(pcTag, end, CERTIFICATE, sizeof(CERTIFICATE) - 1))
            {
                bt = fCert;
            }
            else if (buf = IsTag(pcTag, end, 
                ENCRYPTED_PRIVATE_KEY + _countof("ENCRYPTED"), 
                sizeof(ENCRYPTED_PRIVATE_KEY) - _countof("ENCRYPTED") - 1))
            {
                bt = fPrivKey;
            }
            else if (buf = IsTag(pcTag, end, ENCRYPTED_PRIVATE_KEY, sizeof(ENCRYPTED_PRIVATE_KEY) - 1))
            {
                bt = fEncPrivKey;
            }
            else
            {
                if (!(buf = _strnstr(pcTag, end - 2, _____)))
                {
                    break;
                }
                goto __0;
            }

            if (!(buf = IsTag(buf, end - 2, _____, sizeof(_____) - 1)))
            {
                break;
            }

__0:
            ULONG len = RtlPointerToOffset(pcTag, buf);

            PCSTR pc = buf;

            if (!(buf = _strnstr(buf, end, _____)))
            {
                break;
            }

            ULONG cb = RtlPointerToOffset(pc, buf - sizeof(_____));

            if (!(buf = IsEnd(buf, end - sizeof(_____))) || 
                *buf++ != ' ' ||
                !(buf = IsTag(buf, end, pcTag, len)))
            {
                break;
            }

            UCHAR pembuf[sizeof(PemEncryptedPrivateKey)];
            Pem* pem = 0;

            switch (bt)
            {
            case fCert:
                pem = new(pembuf, sizeof(pembuf)) PemCert(hStore);
                break;

            case fPrivKey:
                pem = new(pembuf, sizeof(pembuf)) PemPrivateKey;
                break;

            case fEncPrivKey:
                pem = new(pembuf, sizeof(pembuf)) PemEncryptedPrivateKey(pszPassword);
                break;
            }

            if (pem)
            {
                if (S_OK == (hr = pem->import(pc, cb)))
                {
                    if (fCert == bt)
                    {
                        nCerts++;
                    }
                    else
                    {
                        if (--keys < stack)
                        {
                            stack = alloca(sizeof(Pem*));
                        }

                        *keys = static_cast<PemKey*>(pem)->get();
                        
                        if (++nKeys == 128)
                        {
                            hr = STATUS_TOO_MANY_SECRETS;
                        }
                    }
                }

                delete pem;
            }

            if (hr)
            {
                break;
            }
        }

        if (S_OK == hr)
        {
            if (!nKeys && !nCerts)
            {
                hr = STATUS_NOT_FOUND;
            }
            else
            {
                if (nKeys)
                {
                    if (PVOID pv = HR(hr, LocalAlloc(LMEM_FIXED, nKeys * sizeof(BCRYPT_KEY_HANDLE))))
                    {
                        *pphKeys = (BCRYPT_KEY_HANDLE *)memcpy(pv, keys, nKeys * sizeof(BCRYPT_KEY_HANDLE));
                        *pnKeys = nKeys;
                    }
                }

                if (S_OK == hr)
                {
                    *phStore = hStore;
                    return S_OK;
                }
            }
        }

        FreeKeysI(keys, nKeys);
        CertCloseStore(hStore, 0);
    }

    return hr;
}
© www.soinside.com 2019 - 2024. All rights reserved.