我使用
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 链接来加载公钥。你知道我做错了什么吗?我应该以特定方式生成密钥吗?
请将“pkcs1”字符串替换为“spki”。该代码不需要 RSA 密钥,它需要一个SubjectPublicKeyInfo 编码的密钥。
SPKI是在X.509标准中定义的,它允许ASN.1编码来指示任何类型的密钥。例如,它还可以包含各种类型的基于椭圆曲线的密钥。它在内部包含 PKCS#1 编码密钥,但在本例中,密钥类型的指示符(在本例中为 RSA 密钥)采用二进制编码而不是文本编码。
备注:
BCRYPT_PAD_NONE
仅在您定义自己的方案或实施另一个现有方案时才有用,这可能是最好避免的。如果您想加密更大的消息,请考虑混合加密(例如加密 AES 密钥)。不同的 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;
}