英文:
RSA Decryption from c++ Windows Crypto API to Golang
问题
我已经使用Windows Crypto API使用RSA公钥对字符串进行了加密,但是我无法使用Go的RSA私钥对其进行解密。
以下是使用C++进行加密的代码:
HCRYPTPROV hCryptProv = NULL;
HCRYPTKEY hKey = NULL;
DWORD dwPublicKeyLen = 0;
DWORD dwDataLen = 0;
DWORD dwEncryptedLen = 0;
BYTE* pbPublicKey = NULL;
BYTE* pbData = NULL;
HANDLE hPublicKeyFile = NULL;
HANDLE hEncryptedFile = NULL;
HANDLE hPlainFile = NULL;
DWORD lpNumberOfBytesWritten = 0;
BYTE derPubKey[2048];
DWORD derPubKeyLen = 2048;
CERT_PUBLIC_KEY_INFO* publicKeyInfo = NULL;
DWORD publicKeyInfoLen = 0;
HANDLE hFile = NULL;
if (!CryptAcquireContextW(&hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
{
printf("CryptAcquireContext error 0x%x\n", GetLastError());
return 1;
}
std::string pempubkeyS = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNoGP0DHZA9RyZlQETr3NGr6hoxn9oHFiFeJwCUooz+qP38vQ53Cs7VtisfEl/FmkwRCz9l0bKU9MZ00Z1/WLTa+48dqGMNL2+um1za0Z0fyxXmYEwy3zvFaswtgzHfXlN+pcay6DsaBXXSvQpC6sz50DvcIw4YsMPqSSBk++LSQIDAQAB";
std::wstring pempubkey = std::wstring(pempubkeyS.begin(), pempubkeyS.end());
if (!CryptStringToBinaryW(pempubkey.c_str(), 0, CRYPT_STRING_BASE64, derPubKey, &derPubKeyLen, NULL, NULL))
{
printf("CryptStringToBinary failed. Err: %d\n", GetLastError());
return -1;
}
/*
* Decode from DER format to CERT_PUBLIC_KEY_INFO
*/
if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, derPubKey, derPubKeyLen, CRYPT_ENCODE_ALLOC_FLAG, NULL, &publicKeyInfo, &publicKeyInfoLen))
{
printf("CryptDecodeObjectEx 1 failed. Err: %x\n", GetLastError());
return -1;
}
/*
* Import the public key using the context
*/
if (!CryptImportPublicKeyInfo(hCryptProv, X509_ASN_ENCODING, publicKeyInfo, &hKey))
{
printf("CryptImportPublicKeyInfo failed. error: %d\n", GetLastError());
return -1;
}
LocalFree(publicKeyInfo);
std::string cleartext = "This is a test!";
int len = cleartext.length();
// Get lenght for encrypted data
if (!CryptEncrypt(hKey, NULL, TRUE, CRYPT_OAEP, NULL, &dwEncryptedLen, 0))
{
// Error
printf("CryptEncrypt error 0x%x\n", GetLastError());
return 1;
}
cleartext.resize(len + dwEncryptedLen);
// Encrypt data
if (!CryptEncrypt(hKey, NULL, TRUE, 0, (BYTE*)&cleartext[0], &dwDataLen, dwEncryptedLen))
{
// Error
printf("CryptEncrypt error 0x%x\n", GetLastError());
return 1;
}
std::string cleartextbase64 = "";
cleartext.resize(dwDataLen);
Base64Encode(cleartext, &cleartextbase64);
std::cout << cleartextbase64 << std::endl;
// HkCebflDgmxJ33hmLTrqGsPyyyPKP74MePHedzrB8jiI6AOJhIw06WD93HggIRCgm/A6CqRYYgHe749Z6uTAqsh2dY9bvGMfNGLAQ7g5YFYlK+MWUEyFB1yRH6cDJKloP+J1UhIibGa3R+cwY9EHfDNCZfeTL0zYPHFKun9OBZ0=
以下是使用Go进行解密的代码:
keyRaw, err := base64.StdEncoding.DecodeString("LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlDWHdJQkFBS0JnUUROb0dQMERIWkE5UnlabFFFVHIzTkdyNmhveG45b0hGaUZlSndDVW9veitxUDM4dlE1CjNDczdWdGlzZkVsL0Zta3dSQ3o5bDBiS1U5TVowMFoxL1dMVGErNDhkcUdNTkwyK3VtMXphMFowZnl4WG1ZRXcKeTN6dkZhc3d0Z3pIZlhsTitwY2F5NkRzYUJYWFN2UXBDNnN6NTBEdmNJdzRZc01QcVNTQmsrK0xTUUlEQVFBQgpBb0dCQUtia3J6dTlnWjFuVkRjelFSU0JLc2NNZTF2UEFFbTMrQUVjeTBMM1MwUzFBYkNWZUxRZGh0azZ6CkxhR3hKM
<details>
<summary>英文:</summary>
I have encrypted a string with RSA public key using the Windows Crypto API, but I'm not able to decrypt it using the RSA private key with Go.
The following code is the encryption using C++
<!-- language: lang-cpp -->
HCRYPTPROV hCryptProv = NULL;
HCRYPTKEY hKey = NULL;
DWORD dwPublicKeyLen = 0;
DWORD dwDataLen = 0;
DWORD dwEncryptedLen = 0;
BYTE* pbPublicKey = NULL;
BYTE* pbData = NULL;
HANDLE hPublicKeyFile = NULL;
HANDLE hEncryptedFile = NULL;
HANDLE hPlainFile = NULL;
DWORD lpNumberOfBytesWritten = 0;
BYTE derPubKey[2048];
DWORD derPubKeyLen = 2048;
CERT_PUBLIC_KEY_INFO* publicKeyInfo = NULL;
DWORD publicKeyInfoLen = 0;
HANDLE hFile = NULL;
if (!CryptAcquireContextW(&hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
{
printf("CryptAcquireContext error 0x%x\n", GetLastError());
return 1;
}
std::string pempubkeyS = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNoGP0DHZA9RyZlQETr3NGr6hoxn9oHFiFeJwCUooz+qP38vQ53Cs7VtisfEl/FmkwRCz9l0bKU9MZ00Z1/WLTa+48dqGMNL2+um1za0Z0fyxXmYEwy3zvFaswtgzHfXlN+pcay6DsaBXXSvQpC6sz50DvcIw4YsMPqSSBk++LSQIDAQAB";
std::wstring pempubkey = std::wstring(pempubkeyS.begin(), pempubkeyS.end());
if (!CryptStringToBinaryW(pempubkey.c_str(), 0, CRYPT_STRING_BASE64, derPubKey, &derPubKeyLen, NULL, NULL))
{
printf("CryptStringToBinary failed. Err: %d\n", GetLastError());
return -1;
}
/*
* Decode from DER format to CERT_PUBLIC_KEY_INFO
*/
if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, derPubKey, derPubKeyLen, CRYPT_ENCODE_ALLOC_FLAG, NULL, &publicKeyInfo, &publicKeyInfoLen))
{
printf("CryptDecodeObjectEx 1 failed. Err: %x\n", GetLastError());
return -1;
}
/*
* Import the public key using the context
*/
if (!CryptImportPublicKeyInfo(hCryptProv, X509_ASN_ENCODING, publicKeyInfo, &hKey))
{
printf("CryptImportPublicKeyInfo failed. error: %d\n", GetLastError());
return -1;
}
LocalFree(publicKeyInfo);
std::string cleartext = "This is a test!";
int len = cleartext.length();
// Get lenght for encrypted data
if (!CryptEncrypt(hKey, NULL, TRUE, CRYPT_OAEP, NULL, &dwEncryptedLen, 0))
{
// Error
printf("CryptEncrypt error 0x%x\n", GetLastError());
return 1;
}
cleartext.resize(len + dwEncryptedLen);
// Encrypt data
if (!CryptEncrypt(hKey, NULL, TRUE, 0, (BYTE*)&cleartext[0], &dwDataLen, dwEncryptedLen))
{
// Error
printf("CryptEncrypt error 0x%x\n", GetLastError());
return 1;
}
std::string cleartextbase64 = "";
cleartext.resize(dwDataLen);
Base64Encode(cleartext, &cleartextbase64);
std::cout << cleartextbase64 << std::endl;
// HkCebflDgmxJ33hmLTrqGsPyyyPKP74MePHedzrB8jiI6AOJhIw06WD93HggIRCgm/A6CqRYYgHe749Z6uTAqsh2dY9bvGMfNGLAQ7g5YFYlK+MWUEyFB1yRH6cDJKloP+J1UhIibGa3R+cwY9EHfDNCZfeTL0zYPHFKun9OBZ0=
The following code is the decryption using Go
<!-- language: lang-go -->
keyRaw, err := base64.StdEncoding.DecodeString("LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlDWHdJQkFBS0JnUUROb0dQMERIWkE5UnlabFFFVHIzTkdyNmhveG45b0hGaUZlSndDVW9veitxUDM4dlE1CjNDczdWdGlzZkVsL0Zta3dSQ3o5bDBiS1U5TVowMFoxL1dMVGErNDhkcUdNTkwyK3VtMXphMFowZnl4WG1ZRXcKeTN6dkZhc3d0Z3pIZlhsTitwY2F5NkRzYUJYWFN2UXBDNnN6NTBEdmNJdzRZc01QcVNTQmsrK0xTUUlEQVFBQgpBb0dCQUtia3J6dTlnWjFuVkRjelFSU0JLc2NNZTF2UEFFbTMrQUVjeTBMM1MwUzFBYkNWZUxRZGh0azZ1OUlECmJvTy81TkJRQlZRdUhEN0xtbU16bjlUVVBBaDRRWUxSUGxVQWFRVTM5eThxTDdIbWdoR0lRN0JrZWxPS3hYQjkKNEtBUTFiRksxZ2hwZEFEeDhtTVh5SWZjTG5JSnRJT1ZibWloRjNxdUt4UzBuRlNCQWtFQTE2TlRPeWpKWW9aUQpoaG9XSFJHcXlDU1ZKbGRwaUxSaENxNFAwRVNER2NaTFRlMVVSVXRhRWlZeHk4cGdoQmkxVHpkU1ZVM3h2bk10ClZOeEtoc0tHblFKQkFQUWRXUzdXYUFvWHJYODZ1alJNd1VxcW4zcVJwMWVGM29hdzZ0TWtWZHpDcTZoRzlyRjUKU2pzekhjUkJ4WlNyeklWeXN1T2pBYS9ta0JtbmZtV3Y0WjBDUVFDMy9DMXVvMzA0S0J1YVg3V1FkZHQrU3VCTApSM2ZPNFFDUGFUWXEzOW52NnVXamhxUkpQMktKYTdjL0J0eFV1UFF4czZUM0RicitZUzFEWTNYZkJ5aHRBa0VBCjZwZ05yYkpFZDNaN3VDb3k2YkhkaTZqZTdBWnZuKys1Z3cwZ0RscjczTlNEN0lxTjVzNGQ1VGhoWWNxbld4R2kKMFpnQmpEdUprb1pyY3d3QXJ5NVFEUUpCQUt0Uk1LbmJnZFR3TWRQbTMwQWErQk50UGNwckN4VXFFWHpCT2lZSQpXVVBZOTBCdmxGSzkvK3VVWktOQzhoK1Ntc2F3ekFaKzZUVzlEWUFuSWZ1UkExZz0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K")
if err != nil {
panic(err)
}
block, _ := pem.Decode(keyRaw)
configPrivateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
panic(err)
}
encodedEncrypted := `HkCebflDgmxJ33hmLTrqGsPyyyPKP74MePHedzrB8jiI6AOJhIw06WD93HggIRCgm/A6CqRYYgHe749Z6uTAqsh2dY9bvGMfNGLAQ7g5YFYlK+MWUEyFB1yRH6cDJKloP+J1UhIibGa3R+cwY9EHfDNCZfeTL0zYPHFKun9OBZ0=`
cipherText, err := base64.StdEncoding.DecodeString(encodedEncrypted)
if err != nil {
panic(err)
}
cleartextMessage, err := rsa.DecryptPKCS1v15(rand.Reader, configPrivateKey, cipherText)
//cleartextMessage, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, configPrivateKey, cipherText, nil)
if err != nil {
panic(err) // panic: crypto/rsa: decryption error
}
fmt.Println(string(cleartextMessage))
As you can see I've got a *panic: crypto/rsa: decryption error*. Any ideas? Thanks in advance!
</details>
# 答案1
**得分**: 1
有以下问题:
- [`CryptEncrypt()`][2] 返回结果是以 _little_ endian 格式,所以在 Go 代码中,密文必须被反转,例如(参见[这里][1]):
```go
...
for i, j := 0, len(cipherText)-1; i < j; i, j = i+1, j-1 {
cipherText[i], cipherText[j] = cipherText[j], cipherText[i]
}
...
-
在加密(第二个
CryptEncrypt()
调用)时,第六个参数是&dwDataLen
,其中dwDataLen
是0
。根据文档,第六个参数在输入时包含明文长度(在输出时包含密文长度)。因为这里第六个参数在输入时是0
,所以加密的是一个 空 字符串。这可能不是预期的结果,需要相应地修复(即将dwDataLen
设置为明文长度)! -
在加密(第二个
CryptEncrypt()
调用)时,第四个参数是0
,因此应用了 PKCS#1 v1.5 填充。因此,在 Go 代码中,应使用rsa.DecryptPKCS1v15()
,目前已经这样做了(rsa.DecryptOAEP()
被注释掉了)。请注意,在确定加密数据的长度时(第一个CryptEncrypt()
调用),设置了CRYPT_OAEP
标志,这是不一致的,但可能没有影响。这一点已经在注释中提到过。
如果密文被反转,解密将正常工作,并返回一个空字符串作为结果,因为 dwDataLen
等于 0
。
英文:
There are the following issues:
-
CryptEncrypt()
returns the result in little endian format, so in the Go code the ciphertext must be reversed, e.g. (see here):... for i, j := 0, len(cipherText)-1; i < j; i, j = i+1, j-1 { cipherText[i], cipherText[j] = cipherText[j], cipherText[i] } ...
-
On encryption (2nd
CryptEncrypt()
call) the 6th parameter is&dwDataLen
, wheredwDataLen
is0
. According to the documentation, the 6th parameter contains on entry the plaintext length (and on exit the ciphertext length). Because here the 6th parameter on entry is0
, an empty string is encrypted. This is probably not intended and must be fixed accordingly (i.e.dwDataLen
must be set to the plaintext length)! -
On encryption (2nd
CryptEncrypt()
call) the 4th parameter is0
, so PKCS#1 v1.5 padding is applied. Therefore, in the Go code,rsa.DecryptPKCS1v15()
must be used, which is currently the case (rsa.DecryptOAEP()
is commented out). Note that when determining the length of the encrypted data (1stCryptEncrypt()
call), theCRYPT_OAEP
flag is set, which is inconsistent, but probably has no effect. This point has already been addressed in the comments.
If the ciphertext is reversed, the decryption works and returns an empty string as result because of dwDataLen
equals 0
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论