如何使用 CryptoApi 导入 PKCS#8
How to import a PKCS#8 with CryptoApi
我有一个 PKCS#8 密钥,我拼命尝试导入 CryptoAPI 但没有成功。我有:
-----BEGIN PRIVATE KEY-----
<privatekey>
-----END PRIVATE KEY-----
包含这个:
Private Key algo RSA
Private Format PKCS#8
ASN1 Dump
RSA Private CRT Key [.....]
modulus: .....
public exponent: .....
我尝试像这样导入密钥:
if not CryptStringToBinaryA(
PansiChar(aBase64PrivateKey), // pszString: LPCSTR;
length(aBase64PrivateKey), // cchString: DWORD;
CRYPT_STRING_BASE64HEADER, // dwFlags: DWORD;
nil, // pbBinary: pByte;
@cbPrivKey, // pcbBinary: PDWORD;
nil, // pdwSkip: PDWORD;
nil) then raiseLastOsError; // pdwFlags: PDWORD
setlength(pPrivKey, cbPrivKey);
if not CryptStringToBinaryA(
PansiChar(aBase64PrivateKey), // pszString: LPCSTR;
length(aBase64PrivateKey), // cchString: DWORD;
CRYPT_STRING_BASE64HEADER, // dwFlags: DWORD;
@pPrivKey[0], // pbBinary: pByte;
@cbPrivKey, // pcbBinary: PDWORD;
nil, // pdwSkip: PDWORD;
nil) then raiseLastOsError; // pdwFlags: PDWORD
//init pKeyBlob
if not CryptDecodeObjectEx(
X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, // dwCertEncodingType: DWORD;
PKCS_PRIVATE_KEY_INFO, // lpszStructType: LPCSTR;
@pPrivKey[0], // const pbEncoded: PBYTE;
cbPrivKey, // cbEncoded: DWORD;
0, // dwFlags: DWORD;
nil, // pDecodePara: PCRYPT_DECODE_PARA;
nil, // pvStructInfo: Pointer;
@cbKeyBlob) then raiseLastOsError; // pcbStructInfo: PDWORD
setlength(pKeyBlob, cbKeyBlob);
if not CryptDecodeObjectEx(
X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, // dwCertEncodingType: DWORD;
PKCS_PRIVATE_KEY_INFO, // lpszStructType: LPCSTR;
@pPrivKey[0], // const pbEncoded: PBYTE;
cbPrivKey, // cbEncoded: DWORD;
0, // dwFlags: DWORD;
nil, // pDecodePara: PCRYPT_DECODE_PARA;
@pKeyBlob[0], // pvStructInfo: Pointer;
@cbKeyBlob) then raiseLastOsError; // pcbStructInfo: PDWORD
//acquire a handle to a particular key container
if (not CryptAcquireContextA(@hProv, // phProv: PHCRYPTPROV;
nil, // pszContainer: PAnsiChar;
nil, // pszProvider: PAnsiChar;
PROV_RSA_AES, // dwProvType: DWORD;
CRYPT_VERIFYCONTEXT)) then raiselastOsError; // dwFlags: DWORD
try
// Now import the key.
if not CryptImportKey(hProv, // hProv: HCRYPTPROV;
@pKeyBlob[0], // const pbData: PBYTE;
cbKeyBlob, // dwDataLen: DWORD;
0, // hPubKey: HCRYPTKEY;
0, // dwFlags: DWORD;
@hRSAKey) then raiseLastOsError; // phKey: PHCRYPTKEY
但 CryptImportKey
失败并显示 “提供商版本错误”,我猜是因为它正在等待 PKCS#1 密钥。如何导入 PKCS#8 密钥?
用于将 PKCS #8 格式转换为 legacy 或 CNG 加密 api 需要几个步骤。
首先需要通过 CryptStringToBinaryA
和 CRYPT_STRING_BASE64HEADER
将字符串转换为二进制
比用 PKCS_PRIVATE_KEY_INFO
(if private key encrypted need use PKCS_ENCRYPTED_PRIVATE_KEY_INFO
调用 CryptDecodeObjectEx
)
比 再次 调用 CryptDecodeObjectEx
和 PKCS_RSA_PRIVATE_KEY
用于旧加密 api 和 CNG_RSA_PUBLIC_KEY_BLOB
用于 CNG.
现在我们可以调用 BCryptImportKeyPair
或 CryptImportKey
用于导入 public 密钥需要使用 X509_PUBLIC_KEY_INFO
,对于来自证书的 public 密钥 - X509_CERT_TO_BE_SIGNED
(+ CNG_RSA_PUBLIC_KEY_BLOB
对于 CNG ,旧版可以使用 CryptImportPublicKeyInfo
)
说 code 旧版
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
ULONG CryptImportPublicKey(_Out_ HCRYPTKEY *phKey,
_In_ HCRYPTPROV hProv,
_In_ PCUCHAR pbKeyOrCert,
_In_ ULONG cbKeyOrCert,
_In_ bool bCert)
{
ULONG cb;
union {
PVOID pvStructInfo;
PCERT_INFO pCertInfo;
PCERT_PUBLIC_KEY_INFO PublicKeyInfo;
};
ULONG dwError = BOOL_TO_ERROR(CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
bCert ? X509_CERT_TO_BE_SIGNED : X509_PUBLIC_KEY_INFO,
pbKeyOrCert, cbKeyOrCert, CRYPT_DECODE_ALLOC_FLAG|CRYPT_DECODE_NOCOPY_FLAG, 0, &pvStructInfo, &cb));
if (dwError == NOERROR)
{
PVOID pv = pvStructInfo;
if (bCert)
{
PublicKeyInfo = &pCertInfo->SubjectPublicKeyInfo;
}
dwError = BOOL_TO_ERROR(CryptImportPublicKeyInfo(hProv,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PublicKeyInfo, phKey));
LocalFree(pv);
}
return dwError;
}
ULONG CryptImportPrivateKey(_Out_ HCRYPTKEY* phKey,
_In_ HCRYPTPROV hProv,
_In_ PCUCHAR pbKey,
_In_ ULONG cbKey)
{
ULONG cb;
PCRYPT_PRIVATE_KEY_INFO PrivateKeyInfo;
ULONG dwError = BOOL_TO_ERROR(CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_PRIVATE_KEY_INFO,
pbKey, cbKey, CRYPT_DECODE_ALLOC_FLAG|CRYPT_DECODE_NOCOPY_FLAG, 0, (void**)&PrivateKeyInfo, &cb));
if (dwError == NOERROR)
{
PUBLICKEYSTRUC* ppks;
dwError = BOOL_TO_ERROR(CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
PKCS_RSA_PRIVATE_KEY, PrivateKeyInfo->PrivateKey.pbData, PrivateKeyInfo->PrivateKey.cbData,
CRYPT_DECODE_ALLOC_FLAG, 0, (void**)&ppks, &cb));
LocalFree(PrivateKeyInfo);
if (dwError == NOERROR)
{
dwError = BOOL_TO_ERROR(CryptImportKey(hProv, (PUCHAR)ppks, cb, 0, CRYPT_EXPORTABLE, phKey));
LocalFree(ppks);
}
}
return dwError;
}
enum BLOB_TYPE { bt_priv, bt_pub, bt_cert };
ULONG CryptImportKey(_Out_ HCRYPTKEY *phKey,
_In_ HCRYPTPROV hProv,
_In_ BLOB_TYPE bt,
_In_ PCSTR szKey,
_In_ ULONG cchKey)
{
PUCHAR pbKey = 0;
ULONG cbKey = 0;
ULONG dwError;
while (CryptStringToBinaryA(szKey, cchKey, CRYPT_STRING_BASE64HEADER, pbKey, &cbKey, 0, 0))
{
if (pbKey)
{
switch (bt)
{
case bt_priv:
dwError = CryptImportPrivateKey(phKey, hProv, pbKey, cbKey);
break;
case bt_pub:
dwError = CryptImportPublicKey(phKey, hProv, pbKey, cbKey, false);
break;
case bt_cert:
dwError = CryptImportPublicKey(phKey, hProv, pbKey, cbKey, true);
break;
default: dwError = ERROR_INVALID_PARAMETER;
}
_freea(pbKey);
return dwError;
}
if (!(pbKey = (PUCHAR)_malloca(cbKey)))
{
break;
}
}
dwError = GetLastError();
if (pbKey) _freea(pbKey);
return dwError;
}
void DoLegacyTest(_In_ PCSTR szToBeSigned,
_In_ PCSTR szPrivateKey,
_In_ ULONG cchPrivateKey,
_In_ PCSTR szPublicKeyOrCert,
_In_ ULONG cchPublicKeyOrCert,
_In_ bool bCert)
{
HCRYPTPROV hProv;
if (CryptAcquireContextW(&hProv, 0, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
{
HCRYPTKEY hKey;
HCRYPTHASH hHash;
if (CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash))
{
if (CryptHashData(hHash, (PUCHAR)szToBeSigned, (ULONG)strlen(szToBeSigned), 0))
{
PUCHAR pbSignature = 0;
ULONG cbSignature = 0;
BOOL fOk = false;
if (NOERROR == CryptImportKey(&hKey, hProv, bt_priv, szPrivateKey, cchPrivateKey))
{
ULONG dwKeySpec, cb;
if (CryptGetKeyParam(hKey, KP_ALGID, (PUCHAR)&dwKeySpec, &(cb = sizeof(dwKeySpec)), 0))
{
switch (dwKeySpec)
{
case CALG_RSA_KEYX:
dwKeySpec = AT_KEYEXCHANGE;
break;
case CALG_RSA_SIGN:
dwKeySpec = AT_SIGNATURE;
break;
default: dwKeySpec = 0;
}
if (CryptGetKeyParam(hKey, KP_BLOCKLEN, (PUCHAR)&cbSignature, &(cb = sizeof(cbSignature)), 0))
{
pbSignature = (PUCHAR)alloca(cbSignature >>= 3);
fOk = CryptSignHashW(hHash, dwKeySpec, 0, 0, pbSignature, &cbSignature);
}
}
CryptDestroyKey(hKey);
}
if (fOk)
{
if (NOERROR == CryptImportKey(&hKey, hProv, bCert ? bt_cert : bt_pub, szPublicKeyOrCert, cchPublicKeyOrCert))
{
if (!CryptVerifySignatureW(hHash, pbSignature, cbSignature, hKey, 0, 0))
{
__debugbreak();
}
CryptDestroyKey(hKey);
}
}
}
CryptDestroyHash(hHash);
}
CryptReleaseContext(hProv, 0);
}
}
code 对于 CNG
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
NTSTATUS openssl_verify(_In_ BCRYPT_KEY_HANDLE hKey,
_In_ PCUCHAR pbToBeSigned,
_In_ ULONG cbToBeSigned,
_In_ PCUCHAR pbSignature,
_In_ ULONG cbSignature,
_In_ PCWSTR pszAlgId)
{
BCRYPT_ALG_HANDLE hAlgorithm;
NTSTATUS status = BCryptOpenAlgorithmProvider(&hAlgorithm, pszAlgId, 0, 0);
if (0 <= status)
{
BCRYPT_HASH_HANDLE hHash = 0;
ULONG HashBlockLength, cb;
0 <= (status = BCryptGetProperty(hAlgorithm, BCRYPT_HASH_LENGTH, (PUCHAR)&HashBlockLength, sizeof(ULONG), &cb, 0)) &&
0 <= (status = BCryptCreateHash(hAlgorithm, &hHash, 0, 0, 0, 0, 0));
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
if (0 <= status)
{
PUCHAR pbHash = (PUCHAR)alloca(HashBlockLength);
0 <= (status = BCryptHashData(hHash, const_cast<PUCHAR>(pbToBeSigned), cbToBeSigned, 0)) &&
0 <= (status = BCryptFinishHash(hHash, pbHash, HashBlockLength, 0));
BCryptDestroyHash(hHash);
if (0 <= status)
{
BCRYPT_PKCS1_PADDING_INFO pi = { pszAlgId };
status = BCryptVerifySignature(hKey, &pi, pbHash, HashBlockLength,
const_cast<PUCHAR>(pbSignature), cbSignature, BCRYPT_PAD_PKCS1);
}
}
}
return status;
}
inline NTSTATUS openssl_verify(_In_ BCRYPT_KEY_HANDLE hKey,
_In_ PCSTR szToBeSigned,
_In_ PCUCHAR pbSignature,
_In_ ULONG cbSignature,
_In_ PCWSTR pszAlgId)
{
return openssl_verify(hKey, (PCUCHAR)szToBeSigned, (ULONG)strlen(szToBeSigned), pbSignature, cbSignature, pszAlgId);
}
NTSTATUS openssl_sign(_In_ BCRYPT_KEY_HANDLE hKey,
_In_ PCUCHAR pbToBeSigned,
_In_ ULONG cbToBeSigned,
_Out_ PUCHAR pbSignature,
_Inout_ PULONG pcbSignature,
_In_ PCWSTR pszAlgId)
{
BCRYPT_ALG_HANDLE hAlgorithm;
NTSTATUS status = BCryptOpenAlgorithmProvider(&hAlgorithm, pszAlgId, 0, 0);
if (0 <= status)
{
BCRYPT_HASH_HANDLE hHash = 0;
ULONG HashBlockLength, cb;
0 <= (status = BCryptGetProperty(hAlgorithm, BCRYPT_HASH_LENGTH, (PUCHAR)&HashBlockLength, sizeof(ULONG), &cb, 0)) &&
0 <= (status = BCryptCreateHash(hAlgorithm, &hHash, 0, 0, 0, 0, 0));
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
if (0 <= status)
{
PUCHAR pbHash = (PUCHAR)alloca(HashBlockLength);
0 <= (status = BCryptHashData(hHash, const_cast<PUCHAR>(pbToBeSigned), cbToBeSigned, 0)) &&
0 <= (status = BCryptFinishHash(hHash, pbHash, HashBlockLength, 0));
BCryptDestroyHash(hHash);
if (0 <= status)
{
BCRYPT_PKCS1_PADDING_INFO pi = { pszAlgId };
status = BCryptSignHash(hKey, &pi, pbHash, HashBlockLength,
pbSignature, *pcbSignature, pcbSignature, BCRYPT_PAD_PKCS1);
}
}
}
return status;
}
inline NTSTATUS openssl_sign(_In_ BCRYPT_KEY_HANDLE hKey,
_In_ PCSTR szToBeSigned,
_Out_ PUCHAR pbSignature,
_Inout_ PULONG pcbSignature,
_In_ PCWSTR pszAlgId)
{
return openssl_sign(hKey, (PCUCHAR)szToBeSigned, (ULONG)strlen(szToBeSigned), pbSignature, pcbSignature, pszAlgId);
}
NTSTATUS BCryptImportKey(_Out_ BCRYPT_KEY_HANDLE *phKey,
_In_ PCWSTR pszBlobType,
_In_ BCRYPT_RSAKEY_BLOB* prkb,
_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, (PUCHAR)prkb, cb, 0);
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
}
return status;
}
HRESULT BCryptImportPrivateKey(_Out_ BCRYPT_KEY_HANDLE *phKey, _In_ PCUCHAR pbKey, _In_ ULONG cbKey)
{
ULONG cb;
PCRYPT_PRIVATE_KEY_INFO PrivateKeyInfo;
ULONG dwError = BOOL_TO_ERROR(CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_PRIVATE_KEY_INFO,
pbKey, cbKey, CRYPT_DECODE_ALLOC_FLAG|CRYPT_DECODE_NOCOPY_FLAG, 0, (void**)&PrivateKeyInfo, &cb));
if (dwError == NOERROR)
{
BCRYPT_RSAKEY_BLOB* prkb;
dwError = BOOL_TO_ERROR(CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
CNG_RSA_PRIVATE_KEY_BLOB, PrivateKeyInfo->PrivateKey.pbData, PrivateKeyInfo->PrivateKey.cbData,
CRYPT_DECODE_ALLOC_FLAG, 0, (void**)&prkb, &cb));
LocalFree(PrivateKeyInfo);
if (dwError == NOERROR)
{
NTSTATUS status = BCryptImportKey(phKey, BCRYPT_RSAPRIVATE_BLOB, prkb, cb);
LocalFree(prkb);
return HRESULT_FROM_NT(status);
}
}
return HRESULT_FROM_WIN32(dwError);
}
HRESULT BCryptImportPublicKey(_Out_ BCRYPT_KEY_HANDLE *phKey, _In_ PCUCHAR pbKeyOrCert, _In_ ULONG cbKeyOrCert, _In_ bool bCert)
{
ULONG cb;
union {
PVOID pvStructInfo;
PCERT_INFO pCertInfo;
PCERT_PUBLIC_KEY_INFO PublicKeyInfo;
};
ULONG dwError = BOOL_TO_ERROR(CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
bCert ? X509_CERT_TO_BE_SIGNED : X509_PUBLIC_KEY_INFO,
pbKeyOrCert, cbKeyOrCert, CRYPT_DECODE_ALLOC_FLAG|CRYPT_DECODE_NOCOPY_FLAG, 0, &pvStructInfo, &cb));
if (dwError == NOERROR)
{
BCRYPT_RSAKEY_BLOB* prkb;
PVOID pv = pvStructInfo;
if (bCert)
{
PublicKeyInfo = &pCertInfo->SubjectPublicKeyInfo;
}
dwError = BOOL_TO_ERROR(CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
CNG_RSA_PUBLIC_KEY_BLOB,
PublicKeyInfo->PublicKey.pbData,
PublicKeyInfo->PublicKey.cbData,
CRYPT_DECODE_ALLOC_FLAG, 0, (void**)&prkb, &cb));
LocalFree(pv);
if (dwError == NOERROR)
{
NTSTATUS status = BCryptImportKey(phKey, BCRYPT_RSAPUBLIC_BLOB, prkb, cb);
LocalFree(prkb);
return HRESULT_FROM_NT(status);
}
}
return HRESULT_FROM_WIN32(dwError);
}
enum BLOB_TYPE { bt_priv, bt_pub, bt_cert };
HRESULT BCryptImportKey(_Out_ BCRYPT_KEY_HANDLE *phKey, _In_ BLOB_TYPE bt, _In_ PCSTR szKey, _In_ ULONG cchKey)
{
PUCHAR pbKey = 0;
ULONG cbKey = 0;
HRESULT hr;
while (CryptStringToBinaryA(szKey, cchKey, CRYPT_STRING_BASE64HEADER, pbKey, &cbKey, 0, 0))
{
if (pbKey)
{
switch (bt)
{
case bt_priv:
hr = BCryptImportPrivateKey(phKey, pbKey, cbKey);
break;
case bt_pub:
hr = BCryptImportPublicKey(phKey, pbKey, cbKey, false);
break;
case bt_cert:
hr = BCryptImportPublicKey(phKey, pbKey, cbKey, true);
break;
default: hr = E_INVALIDARG;
}
_freea(pbKey);
return hr;
}
if (!(pbKey = (PUCHAR)_malloca(cbKey)))
{
break;
}
}
hr = HRESULT_FROM_WIN32(GetLastError());
if (pbKey) _freea(pbKey);
return hr;
}
void DoCNGTest(_In_ PCSTR szToBeSigned,
_In_ PCSTR szPrivateKey,
_In_ ULONG cchPrivateKey,
_In_ PCSTR szPublicKeyOrCert,
_In_ ULONG cchPublicKeyOrCert,
_In_ bool bCert)
{
HRESULT hr;
BCRYPT_KEY_HANDLE hKey;
PUCHAR pbSignature = 0;
ULONG cbSignature = 0, cb;
if (0 <= (hr = BCryptImportKey(&hKey, bt_priv, szPrivateKey, cchPrivateKey)))
{
if (0 <= (hr = BCryptGetProperty(hKey, BCRYPT_SIGNATURE_LENGTH, (PUCHAR)&cbSignature, sizeof(ULONG), &cb, 0)))
{
pbSignature = (PUCHAR)alloca(cbSignature);
hr = HRESULT_FROM_NT(openssl_sign(hKey, szToBeSigned, pbSignature, &cbSignature, BCRYPT_SHA256_ALGORITHM));
}
BCryptDestroyKey(hKey);
}
if (0 <= hr)
{
if (0 <= (hr = BCryptImportKey(&hKey, bCert ? bt_cert : bt_pub, szPublicKeyOrCert, cchPublicKeyOrCert)))
{
hr = HRESULT_FROM_NT(openssl_verify(hKey, szToBeSigned, pbSignature, cbSignature, BCRYPT_SHA256_ALGORITHM));
if (0 > hr)
{
__debugbreak();
}
BCryptDestroyKey(hKey);
}
}
}
我有一个 PKCS#8 密钥,我拼命尝试导入 CryptoAPI 但没有成功。我有:
-----BEGIN PRIVATE KEY-----
<privatekey>
-----END PRIVATE KEY-----
包含这个:
Private Key algo RSA
Private Format PKCS#8
ASN1 Dump
RSA Private CRT Key [.....]
modulus: .....
public exponent: .....
我尝试像这样导入密钥:
if not CryptStringToBinaryA(
PansiChar(aBase64PrivateKey), // pszString: LPCSTR;
length(aBase64PrivateKey), // cchString: DWORD;
CRYPT_STRING_BASE64HEADER, // dwFlags: DWORD;
nil, // pbBinary: pByte;
@cbPrivKey, // pcbBinary: PDWORD;
nil, // pdwSkip: PDWORD;
nil) then raiseLastOsError; // pdwFlags: PDWORD
setlength(pPrivKey, cbPrivKey);
if not CryptStringToBinaryA(
PansiChar(aBase64PrivateKey), // pszString: LPCSTR;
length(aBase64PrivateKey), // cchString: DWORD;
CRYPT_STRING_BASE64HEADER, // dwFlags: DWORD;
@pPrivKey[0], // pbBinary: pByte;
@cbPrivKey, // pcbBinary: PDWORD;
nil, // pdwSkip: PDWORD;
nil) then raiseLastOsError; // pdwFlags: PDWORD
//init pKeyBlob
if not CryptDecodeObjectEx(
X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, // dwCertEncodingType: DWORD;
PKCS_PRIVATE_KEY_INFO, // lpszStructType: LPCSTR;
@pPrivKey[0], // const pbEncoded: PBYTE;
cbPrivKey, // cbEncoded: DWORD;
0, // dwFlags: DWORD;
nil, // pDecodePara: PCRYPT_DECODE_PARA;
nil, // pvStructInfo: Pointer;
@cbKeyBlob) then raiseLastOsError; // pcbStructInfo: PDWORD
setlength(pKeyBlob, cbKeyBlob);
if not CryptDecodeObjectEx(
X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, // dwCertEncodingType: DWORD;
PKCS_PRIVATE_KEY_INFO, // lpszStructType: LPCSTR;
@pPrivKey[0], // const pbEncoded: PBYTE;
cbPrivKey, // cbEncoded: DWORD;
0, // dwFlags: DWORD;
nil, // pDecodePara: PCRYPT_DECODE_PARA;
@pKeyBlob[0], // pvStructInfo: Pointer;
@cbKeyBlob) then raiseLastOsError; // pcbStructInfo: PDWORD
//acquire a handle to a particular key container
if (not CryptAcquireContextA(@hProv, // phProv: PHCRYPTPROV;
nil, // pszContainer: PAnsiChar;
nil, // pszProvider: PAnsiChar;
PROV_RSA_AES, // dwProvType: DWORD;
CRYPT_VERIFYCONTEXT)) then raiselastOsError; // dwFlags: DWORD
try
// Now import the key.
if not CryptImportKey(hProv, // hProv: HCRYPTPROV;
@pKeyBlob[0], // const pbData: PBYTE;
cbKeyBlob, // dwDataLen: DWORD;
0, // hPubKey: HCRYPTKEY;
0, // dwFlags: DWORD;
@hRSAKey) then raiseLastOsError; // phKey: PHCRYPTKEY
但 CryptImportKey
失败并显示 “提供商版本错误”,我猜是因为它正在等待 PKCS#1 密钥。如何导入 PKCS#8 密钥?
用于将 PKCS #8 格式转换为 legacy 或 CNG 加密 api 需要几个步骤。
首先需要通过 CryptStringToBinaryA
和 CRYPT_STRING_BASE64HEADER
比用 PKCS_PRIVATE_KEY_INFO
(if private key encrypted need use PKCS_ENCRYPTED_PRIVATE_KEY_INFO
调用 CryptDecodeObjectEx
)
比 再次 调用 CryptDecodeObjectEx
和 PKCS_RSA_PRIVATE_KEY
用于旧加密 api 和 CNG_RSA_PUBLIC_KEY_BLOB
用于 CNG.
现在我们可以调用 BCryptImportKeyPair
或 CryptImportKey
用于导入 public 密钥需要使用 X509_PUBLIC_KEY_INFO
,对于来自证书的 public 密钥 - X509_CERT_TO_BE_SIGNED
(+ CNG_RSA_PUBLIC_KEY_BLOB
对于 CNG ,旧版可以使用 CryptImportPublicKeyInfo
)
说 code 旧版
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
ULONG CryptImportPublicKey(_Out_ HCRYPTKEY *phKey,
_In_ HCRYPTPROV hProv,
_In_ PCUCHAR pbKeyOrCert,
_In_ ULONG cbKeyOrCert,
_In_ bool bCert)
{
ULONG cb;
union {
PVOID pvStructInfo;
PCERT_INFO pCertInfo;
PCERT_PUBLIC_KEY_INFO PublicKeyInfo;
};
ULONG dwError = BOOL_TO_ERROR(CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
bCert ? X509_CERT_TO_BE_SIGNED : X509_PUBLIC_KEY_INFO,
pbKeyOrCert, cbKeyOrCert, CRYPT_DECODE_ALLOC_FLAG|CRYPT_DECODE_NOCOPY_FLAG, 0, &pvStructInfo, &cb));
if (dwError == NOERROR)
{
PVOID pv = pvStructInfo;
if (bCert)
{
PublicKeyInfo = &pCertInfo->SubjectPublicKeyInfo;
}
dwError = BOOL_TO_ERROR(CryptImportPublicKeyInfo(hProv,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PublicKeyInfo, phKey));
LocalFree(pv);
}
return dwError;
}
ULONG CryptImportPrivateKey(_Out_ HCRYPTKEY* phKey,
_In_ HCRYPTPROV hProv,
_In_ PCUCHAR pbKey,
_In_ ULONG cbKey)
{
ULONG cb;
PCRYPT_PRIVATE_KEY_INFO PrivateKeyInfo;
ULONG dwError = BOOL_TO_ERROR(CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_PRIVATE_KEY_INFO,
pbKey, cbKey, CRYPT_DECODE_ALLOC_FLAG|CRYPT_DECODE_NOCOPY_FLAG, 0, (void**)&PrivateKeyInfo, &cb));
if (dwError == NOERROR)
{
PUBLICKEYSTRUC* ppks;
dwError = BOOL_TO_ERROR(CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
PKCS_RSA_PRIVATE_KEY, PrivateKeyInfo->PrivateKey.pbData, PrivateKeyInfo->PrivateKey.cbData,
CRYPT_DECODE_ALLOC_FLAG, 0, (void**)&ppks, &cb));
LocalFree(PrivateKeyInfo);
if (dwError == NOERROR)
{
dwError = BOOL_TO_ERROR(CryptImportKey(hProv, (PUCHAR)ppks, cb, 0, CRYPT_EXPORTABLE, phKey));
LocalFree(ppks);
}
}
return dwError;
}
enum BLOB_TYPE { bt_priv, bt_pub, bt_cert };
ULONG CryptImportKey(_Out_ HCRYPTKEY *phKey,
_In_ HCRYPTPROV hProv,
_In_ BLOB_TYPE bt,
_In_ PCSTR szKey,
_In_ ULONG cchKey)
{
PUCHAR pbKey = 0;
ULONG cbKey = 0;
ULONG dwError;
while (CryptStringToBinaryA(szKey, cchKey, CRYPT_STRING_BASE64HEADER, pbKey, &cbKey, 0, 0))
{
if (pbKey)
{
switch (bt)
{
case bt_priv:
dwError = CryptImportPrivateKey(phKey, hProv, pbKey, cbKey);
break;
case bt_pub:
dwError = CryptImportPublicKey(phKey, hProv, pbKey, cbKey, false);
break;
case bt_cert:
dwError = CryptImportPublicKey(phKey, hProv, pbKey, cbKey, true);
break;
default: dwError = ERROR_INVALID_PARAMETER;
}
_freea(pbKey);
return dwError;
}
if (!(pbKey = (PUCHAR)_malloca(cbKey)))
{
break;
}
}
dwError = GetLastError();
if (pbKey) _freea(pbKey);
return dwError;
}
void DoLegacyTest(_In_ PCSTR szToBeSigned,
_In_ PCSTR szPrivateKey,
_In_ ULONG cchPrivateKey,
_In_ PCSTR szPublicKeyOrCert,
_In_ ULONG cchPublicKeyOrCert,
_In_ bool bCert)
{
HCRYPTPROV hProv;
if (CryptAcquireContextW(&hProv, 0, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
{
HCRYPTKEY hKey;
HCRYPTHASH hHash;
if (CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash))
{
if (CryptHashData(hHash, (PUCHAR)szToBeSigned, (ULONG)strlen(szToBeSigned), 0))
{
PUCHAR pbSignature = 0;
ULONG cbSignature = 0;
BOOL fOk = false;
if (NOERROR == CryptImportKey(&hKey, hProv, bt_priv, szPrivateKey, cchPrivateKey))
{
ULONG dwKeySpec, cb;
if (CryptGetKeyParam(hKey, KP_ALGID, (PUCHAR)&dwKeySpec, &(cb = sizeof(dwKeySpec)), 0))
{
switch (dwKeySpec)
{
case CALG_RSA_KEYX:
dwKeySpec = AT_KEYEXCHANGE;
break;
case CALG_RSA_SIGN:
dwKeySpec = AT_SIGNATURE;
break;
default: dwKeySpec = 0;
}
if (CryptGetKeyParam(hKey, KP_BLOCKLEN, (PUCHAR)&cbSignature, &(cb = sizeof(cbSignature)), 0))
{
pbSignature = (PUCHAR)alloca(cbSignature >>= 3);
fOk = CryptSignHashW(hHash, dwKeySpec, 0, 0, pbSignature, &cbSignature);
}
}
CryptDestroyKey(hKey);
}
if (fOk)
{
if (NOERROR == CryptImportKey(&hKey, hProv, bCert ? bt_cert : bt_pub, szPublicKeyOrCert, cchPublicKeyOrCert))
{
if (!CryptVerifySignatureW(hHash, pbSignature, cbSignature, hKey, 0, 0))
{
__debugbreak();
}
CryptDestroyKey(hKey);
}
}
}
CryptDestroyHash(hHash);
}
CryptReleaseContext(hProv, 0);
}
}
code 对于 CNG
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
NTSTATUS openssl_verify(_In_ BCRYPT_KEY_HANDLE hKey,
_In_ PCUCHAR pbToBeSigned,
_In_ ULONG cbToBeSigned,
_In_ PCUCHAR pbSignature,
_In_ ULONG cbSignature,
_In_ PCWSTR pszAlgId)
{
BCRYPT_ALG_HANDLE hAlgorithm;
NTSTATUS status = BCryptOpenAlgorithmProvider(&hAlgorithm, pszAlgId, 0, 0);
if (0 <= status)
{
BCRYPT_HASH_HANDLE hHash = 0;
ULONG HashBlockLength, cb;
0 <= (status = BCryptGetProperty(hAlgorithm, BCRYPT_HASH_LENGTH, (PUCHAR)&HashBlockLength, sizeof(ULONG), &cb, 0)) &&
0 <= (status = BCryptCreateHash(hAlgorithm, &hHash, 0, 0, 0, 0, 0));
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
if (0 <= status)
{
PUCHAR pbHash = (PUCHAR)alloca(HashBlockLength);
0 <= (status = BCryptHashData(hHash, const_cast<PUCHAR>(pbToBeSigned), cbToBeSigned, 0)) &&
0 <= (status = BCryptFinishHash(hHash, pbHash, HashBlockLength, 0));
BCryptDestroyHash(hHash);
if (0 <= status)
{
BCRYPT_PKCS1_PADDING_INFO pi = { pszAlgId };
status = BCryptVerifySignature(hKey, &pi, pbHash, HashBlockLength,
const_cast<PUCHAR>(pbSignature), cbSignature, BCRYPT_PAD_PKCS1);
}
}
}
return status;
}
inline NTSTATUS openssl_verify(_In_ BCRYPT_KEY_HANDLE hKey,
_In_ PCSTR szToBeSigned,
_In_ PCUCHAR pbSignature,
_In_ ULONG cbSignature,
_In_ PCWSTR pszAlgId)
{
return openssl_verify(hKey, (PCUCHAR)szToBeSigned, (ULONG)strlen(szToBeSigned), pbSignature, cbSignature, pszAlgId);
}
NTSTATUS openssl_sign(_In_ BCRYPT_KEY_HANDLE hKey,
_In_ PCUCHAR pbToBeSigned,
_In_ ULONG cbToBeSigned,
_Out_ PUCHAR pbSignature,
_Inout_ PULONG pcbSignature,
_In_ PCWSTR pszAlgId)
{
BCRYPT_ALG_HANDLE hAlgorithm;
NTSTATUS status = BCryptOpenAlgorithmProvider(&hAlgorithm, pszAlgId, 0, 0);
if (0 <= status)
{
BCRYPT_HASH_HANDLE hHash = 0;
ULONG HashBlockLength, cb;
0 <= (status = BCryptGetProperty(hAlgorithm, BCRYPT_HASH_LENGTH, (PUCHAR)&HashBlockLength, sizeof(ULONG), &cb, 0)) &&
0 <= (status = BCryptCreateHash(hAlgorithm, &hHash, 0, 0, 0, 0, 0));
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
if (0 <= status)
{
PUCHAR pbHash = (PUCHAR)alloca(HashBlockLength);
0 <= (status = BCryptHashData(hHash, const_cast<PUCHAR>(pbToBeSigned), cbToBeSigned, 0)) &&
0 <= (status = BCryptFinishHash(hHash, pbHash, HashBlockLength, 0));
BCryptDestroyHash(hHash);
if (0 <= status)
{
BCRYPT_PKCS1_PADDING_INFO pi = { pszAlgId };
status = BCryptSignHash(hKey, &pi, pbHash, HashBlockLength,
pbSignature, *pcbSignature, pcbSignature, BCRYPT_PAD_PKCS1);
}
}
}
return status;
}
inline NTSTATUS openssl_sign(_In_ BCRYPT_KEY_HANDLE hKey,
_In_ PCSTR szToBeSigned,
_Out_ PUCHAR pbSignature,
_Inout_ PULONG pcbSignature,
_In_ PCWSTR pszAlgId)
{
return openssl_sign(hKey, (PCUCHAR)szToBeSigned, (ULONG)strlen(szToBeSigned), pbSignature, pcbSignature, pszAlgId);
}
NTSTATUS BCryptImportKey(_Out_ BCRYPT_KEY_HANDLE *phKey,
_In_ PCWSTR pszBlobType,
_In_ BCRYPT_RSAKEY_BLOB* prkb,
_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, (PUCHAR)prkb, cb, 0);
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
}
return status;
}
HRESULT BCryptImportPrivateKey(_Out_ BCRYPT_KEY_HANDLE *phKey, _In_ PCUCHAR pbKey, _In_ ULONG cbKey)
{
ULONG cb;
PCRYPT_PRIVATE_KEY_INFO PrivateKeyInfo;
ULONG dwError = BOOL_TO_ERROR(CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_PRIVATE_KEY_INFO,
pbKey, cbKey, CRYPT_DECODE_ALLOC_FLAG|CRYPT_DECODE_NOCOPY_FLAG, 0, (void**)&PrivateKeyInfo, &cb));
if (dwError == NOERROR)
{
BCRYPT_RSAKEY_BLOB* prkb;
dwError = BOOL_TO_ERROR(CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
CNG_RSA_PRIVATE_KEY_BLOB, PrivateKeyInfo->PrivateKey.pbData, PrivateKeyInfo->PrivateKey.cbData,
CRYPT_DECODE_ALLOC_FLAG, 0, (void**)&prkb, &cb));
LocalFree(PrivateKeyInfo);
if (dwError == NOERROR)
{
NTSTATUS status = BCryptImportKey(phKey, BCRYPT_RSAPRIVATE_BLOB, prkb, cb);
LocalFree(prkb);
return HRESULT_FROM_NT(status);
}
}
return HRESULT_FROM_WIN32(dwError);
}
HRESULT BCryptImportPublicKey(_Out_ BCRYPT_KEY_HANDLE *phKey, _In_ PCUCHAR pbKeyOrCert, _In_ ULONG cbKeyOrCert, _In_ bool bCert)
{
ULONG cb;
union {
PVOID pvStructInfo;
PCERT_INFO pCertInfo;
PCERT_PUBLIC_KEY_INFO PublicKeyInfo;
};
ULONG dwError = BOOL_TO_ERROR(CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
bCert ? X509_CERT_TO_BE_SIGNED : X509_PUBLIC_KEY_INFO,
pbKeyOrCert, cbKeyOrCert, CRYPT_DECODE_ALLOC_FLAG|CRYPT_DECODE_NOCOPY_FLAG, 0, &pvStructInfo, &cb));
if (dwError == NOERROR)
{
BCRYPT_RSAKEY_BLOB* prkb;
PVOID pv = pvStructInfo;
if (bCert)
{
PublicKeyInfo = &pCertInfo->SubjectPublicKeyInfo;
}
dwError = BOOL_TO_ERROR(CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
CNG_RSA_PUBLIC_KEY_BLOB,
PublicKeyInfo->PublicKey.pbData,
PublicKeyInfo->PublicKey.cbData,
CRYPT_DECODE_ALLOC_FLAG, 0, (void**)&prkb, &cb));
LocalFree(pv);
if (dwError == NOERROR)
{
NTSTATUS status = BCryptImportKey(phKey, BCRYPT_RSAPUBLIC_BLOB, prkb, cb);
LocalFree(prkb);
return HRESULT_FROM_NT(status);
}
}
return HRESULT_FROM_WIN32(dwError);
}
enum BLOB_TYPE { bt_priv, bt_pub, bt_cert };
HRESULT BCryptImportKey(_Out_ BCRYPT_KEY_HANDLE *phKey, _In_ BLOB_TYPE bt, _In_ PCSTR szKey, _In_ ULONG cchKey)
{
PUCHAR pbKey = 0;
ULONG cbKey = 0;
HRESULT hr;
while (CryptStringToBinaryA(szKey, cchKey, CRYPT_STRING_BASE64HEADER, pbKey, &cbKey, 0, 0))
{
if (pbKey)
{
switch (bt)
{
case bt_priv:
hr = BCryptImportPrivateKey(phKey, pbKey, cbKey);
break;
case bt_pub:
hr = BCryptImportPublicKey(phKey, pbKey, cbKey, false);
break;
case bt_cert:
hr = BCryptImportPublicKey(phKey, pbKey, cbKey, true);
break;
default: hr = E_INVALIDARG;
}
_freea(pbKey);
return hr;
}
if (!(pbKey = (PUCHAR)_malloca(cbKey)))
{
break;
}
}
hr = HRESULT_FROM_WIN32(GetLastError());
if (pbKey) _freea(pbKey);
return hr;
}
void DoCNGTest(_In_ PCSTR szToBeSigned,
_In_ PCSTR szPrivateKey,
_In_ ULONG cchPrivateKey,
_In_ PCSTR szPublicKeyOrCert,
_In_ ULONG cchPublicKeyOrCert,
_In_ bool bCert)
{
HRESULT hr;
BCRYPT_KEY_HANDLE hKey;
PUCHAR pbSignature = 0;
ULONG cbSignature = 0, cb;
if (0 <= (hr = BCryptImportKey(&hKey, bt_priv, szPrivateKey, cchPrivateKey)))
{
if (0 <= (hr = BCryptGetProperty(hKey, BCRYPT_SIGNATURE_LENGTH, (PUCHAR)&cbSignature, sizeof(ULONG), &cb, 0)))
{
pbSignature = (PUCHAR)alloca(cbSignature);
hr = HRESULT_FROM_NT(openssl_sign(hKey, szToBeSigned, pbSignature, &cbSignature, BCRYPT_SHA256_ALGORITHM));
}
BCryptDestroyKey(hKey);
}
if (0 <= hr)
{
if (0 <= (hr = BCryptImportKey(&hKey, bCert ? bt_cert : bt_pub, szPublicKeyOrCert, cchPublicKeyOrCert)))
{
hr = HRESULT_FROM_NT(openssl_verify(hKey, szToBeSigned, pbSignature, cbSignature, BCRYPT_SHA256_ALGORITHM));
if (0 > hr)
{
__debugbreak();
}
BCryptDestroyKey(hKey);
}
}
}