I have signed a data in windows using wincrypt cryptoapi (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING) and in linux, I have x509 certificate and the signed message which i have to verify
Code in windows to sign :
hStoreHandle = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
CERT_PERSONAL_STORE_NAME
);
CheckError((BOOL)hStoreHandle, L"CertOpenStore....................... ");
// Get signer's certificate with access to private key.
do {
// Get a certificate that matches the search criteria
pSignerCert = CertFindCertificateInStore(
hStoreHandle,
MY_TYPE,
0,
CERT_FIND_SUBJECT_STR,
SignerName,
pSignerCert
);
CheckError((BOOL)pSignerCert, L"CertFindCertificateInStore.......... ");
// Get the CSP, and check if we can sign with the private key
bResult = CryptAcquireCertificatePrivateKey(
pSignerCert,
0,
NULL,
&hCryptProv,
&dwKeySpec,
NULL
);
CheckError(bResult, L"CryptAcquireCertificatePrivateKey... ");
} while ((dwKeySpec & AT_SIGNATURE) != AT_SIGNATURE);
// Create the hash object.
bResult = CryptCreateHash(
hCryptProv,
CALG_MD5,
0,
0,
&hHash
);
CheckError(bResult, L"CryptCreateHash..................... ");
// Open the file with the content to be signed
hDataFile = CreateFileW(DataFileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL
);
CheckError((hDataFile != INVALID_HANDLE_VALUE), L"CreateFile.......................... ");
// Compute the cryptographic hash of the data.
while (bResult = ReadFile(hDataFile, rgbFile, BUFSIZE, &cbRead, NULL))
{
if (cbRead == 0)
{
break;
}
CheckError(bResult, L"ReadFile............................ ");
bResult = CryptHashData(
hHash,
rgbFile,
cbRead,
0
);
CheckError(bResult, L"CryptHashData....................... ");
}
CheckError(bResult, L"ReadFile............................ ");
// Sign the hash object
dwSigLen = 0;
bResult = CryptSignHash(
hHash,
AT_SIGNATURE,
NULL,
0,
NULL,
&dwSigLen
);
CheckError(bResult, L"CryptSignHash....................... ");
pbSignature = (BYTE *)malloc(dwSigLen);
CheckError((BOOL)pbSignature, L"malloc.............................. ");
bResult = CryptSignHash(
hHash,
AT_SIGNATURE,
NULL,
0,
pbSignature,
&dwSigLen
);
CheckError(bResult, L"CryptSignHash....................... ");
// Create a file to save the signature
hSignatureFile = CreateFileW(
SignatureFileName,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
CheckError((hSignatureFile != INVALID_HANDLE_VALUE), L"CreateFile.......................... ");
// Write the signature to the file
bResult = WriteFile(
hSignatureFile,
(LPCVOID)pbSignature,
dwSigLen,
&lpNumberOfBytesWritten,
NULL
);
CheckError(bResult, L"WriteFile........................... ");
In openssl i tried:
openssl rsautl -verify -inkey pubkey.pem -keyform PEM -pubin -in signedmessage
it is throwing error::
RSA operation error
4296:error:0406706C:rsa routines:RSA_EAY_PUBLIC_DECRYPT:data greater than modlen:fips_rsa_eay.c:709:
and this error if the signedmessage is hashed
RSA operation error
4432:error:0407006A:rsa routines:RSA_padding_check_PKCS1_type_1:block type is not 01:rsa_pk1.c:100:
4432:error:04067072:rsa routines:RSA_EAY_PUBLIC_DECRYPT:padding check failed:fips_rsa_eay.c:748:
i also tried :
openssl dgst -verify pubkey.pem -signature signedmessage
but program goes into infinite loop
I also find one command:
int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store, BIO *indata, BIO *out, int flags);
but it require too many argument of which i am not aware of.e.g there is no x509_store, crlfile used in this
. can any one tell me how to verify the signed message
I get x509 pem certificate and signedmessage as input in linux which i have to verify
After some example by mail, we got to the following recipe
setup:
we have a x509 certificate cert.p7b to start with, a file message.txt, a Windows produced signed.dat, and using sha1 for definiteness.
openssl pkcs7 -inform DER -outform PEM -in cert.p7b -out cert.pem -print_certs
openssl x509 -in cert.pem -noout -pubkey > pubkey.pem
(this need only be done once for a certificate, to get a public key in PEM format)
then reverse signed.dat bytewise to signed.dat.rev
(using a simple C program, or output the bytes differently on Windows, in alternative form)
and finally
openssl dgst -sha1 -verify pubkey.pem -signature signed.dat.rev message.txt
The main problem was the reverse byte order on Windows (which I have seen before)
Related
I am trying to use the Windows Crypto API to generat an RSA keypair, and then export the public key in either DER or PEM. I have my code working and can successfully export the private key, but I haven't figured out how to do the same with public key. My end goal is to feed the public key to openssl to verify a signature.
I've seen this question: Microsoft CryptoAPI: how to convert PUBLICKEYBLOB to DER/PEM?
But I haven't found a solution. How can I save the public key to a valid PEM format using only native Windows Crypto API functions? What am I missing? Thanks!
Here is the relevant code snippets and my order of API calls for how I am exporting the private key as DER/PEM:
HCRYPTPROV hProv = NULL;
CryptAcquireContext(&hProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0)
HCRYPTKEY hPrivateKey = NULL;
CryptGenKey(hProv, AT_KEYEXCHANGE, CRYPT_ARCHIVABLE, &hPrivateKey);
CryptExportKey(hPrivateKey, 0, PRIVATEKEYBLOB, 0, 0, &size);
BYTE* pPrivKeyBlob = (BYTE *)malloc(size);
CryptExportKey(hPrivateKey, 0, PRIVATEKEYBLOB, 0, pPrivKeyBlob, &size);
CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, pPrivKeyBlob, 0, NULL, NULL, &size);
BYTE* pPrivDer = (BYTE *)malloc(size);
CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, pPrivKeyBlob, 0, NULL, pPrivDer, &size);
This works, and pPrivDer contains the private key in DER format. I can format that to PEM no problem:
DWORD pemSize = 0;
CryptBinaryToStringA(pPrivDer, size, CRYPT_STRING_BASE64HEADER, NULL, &pemSize);
LPSTR pPrivPEM = (LPSTR)malloc(pemSize);
CryptBinaryToStringA(pPrivDer, size, CRYPT_STRING_BASE64HEADER, pPrivPEM, &pemSize);
printf("%s", pPrivPEM);
This works fine and the key is in the correct format (changing the header allows openssl to read/parse it)
However, I can not for the life of me figure out how to save the public key in DER/PEM. This is what I'm trying:
//export public key
CryptExportKey(hPrivateKey, 0, PUBLICKEYBLOB, 0, 0, &size);
BYTE* pPubKeyBlob = (BYTE *)malloc(size);
CryptExportKey(hPrivateKey, 0, PUBLICKEYBLOB, 0, pPubKeyBlob, &size);
CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, mypubkey, 0, NULL, NULL, &size);
BYTE* pPubDer = (BYTE *)malloc(size);
CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, mypubkey, 0, NULL, pPubDer, &size);
DWORD pPubPEMSize = 0;
CryptBinaryToStringA(pPubDer, size, CRYPT_STRING_BASE64, NULL, &pPubPEMSize);
LPSTR pPubPEM = (LPSTR)malloc(pPubPEMSize);
CryptBinaryToStringA(pPubDer, size, CRYPT_STRING_BASE64, pPubPEM, &pPubPEMSize);
printf("-----BEGIN PUBLIC KEY-----\n%s-----END PUBLIC KEY-----\n\n", pPubPEM);
This causes no errors, but the exported DER and printed PEM for the public key is not recognized by openssl and seems to be in the wrong format.
I am trying to use SSL client verification to connect to a remote AWS IoT endpoint. I have three files:
- CA Root Certificate File
- Certificate File
- Private Key
The connection to the endpoint is successful if I use SSL_CTX_load_verify_locations, SSL_CTX_use_certificate_chain_file and SSL_CTX_use_PrivateKey_file.
if (!SSL_CTX_load_verify_locations(p_ssl_context_, root_ca_location_.c_str(), NULL)) {
AWS_LOG_ERROR(OPENSSL_WRAPPER_LOG_TAG, " Root CA Loading error");
return ResponseCode::NETWORK_SSL_ROOT_CRT_PARSE_ERROR;
}
// TODO: streamline error codes for TLS
if (0 < device_cert_location_.length() && 0 < device_private_key_location_.length()) {
AWS_LOG_DEBUG(OPENSSL_WRAPPER_LOG_TAG, "Device crt : %s", device_cert_location_.c_str());
if (!SSL_CTX_use_certificate_chain_file(p_ssl_context_, device_cert_location_.c_str())) {
AWS_LOG_ERROR(OPENSSL_WRAPPER_LOG_TAG, " Device Certificate Loading error");
return ResponseCode::NETWORK_SSL_DEVICE_CRT_PARSE_ERROR;
}
AWS_LOG_DEBUG(OPENSSL_WRAPPER_LOG_TAG, "Device privkey : %s", device_private_key_location_.c_str());
if (1 != SSL_CTX_use_PrivateKey_file(p_ssl_context_,
device_private_key_location_.c_str(),
SSL_FILETYPE_PEM)) {
AWS_LOG_ERROR(OPENSSL_WRAPPER_LOG_TAG, " Device Private Key Loading error");
return ResponseCode::NETWORK_SSL_KEY_PARSE_ERROR;
}
}
certificates_read_flag_ = true;
However, the call doesn't work if I construct the chain manually by adding the certificates using string buffers. The error returned is "An unknown occurred while waiting for the TLS handshake to complete.".
X509_STORE* store = SSL_CTX_get_cert_store(p_ssl_context_);
X509 *rootCACertificate, *deviceCertificate;
RSA *privateKey;
BIO *bio;
std::string rootCACertificateBuffer;
std::string deviceCertificateBuffer;
std::string privateKeyBuffer;
rootCACertificateBuffer =
"-----BEGIN CERTIFICATE-----\n"
"-----END CERTIFICATE-----\n";
deviceCertificateBuffer =
"-----BEGIN CERTIFICATE-----\n"
"-----END CERTIFICATE-----\n";
privateKeyBuffer =
"-----BEGIN RSA PRIVATE KEY-----\n"
"-----END RSA PRIVATE KEY-----\n";
bio = BIO_new(BIO_s_mem());
BIO_puts(bio, rootCACertificateBuffer.c_str());
rootCACertificate = PEM_read_bio_X509(bio, NULL, 0, NULL);
X509_STORE_add_cert(store, rootCACertificate);
BIO_free(bio);
bio = BIO_new(BIO_s_mem());
BIO_puts(bio, deviceCertificateBuffer.c_str());
deviceCertificate = PEM_read_bio_X509(bio, NULL, 0, NULL);
X509_STORE_add_cert(store, deviceCertificate);
BIO_free(bio);
bio = BIO_new(BIO_s_mem());
BIO_puts(bio, privateKeyBuffer.c_str());
privateKey = PEM_read_bio_RSAPrivateKey(bio, NULL, 0, NULL);
SSL_CTX_use_RSAPrivateKey(p_ssl_context_, privateKey);
BIO_free(bio);
SSL_CTX_build_cert_chain(p_ssl_context_,SSL_BUILD_CHAIN_FLAG_CHECK );
certificates_read_flag_ = true;
I need to sign some data with the SHA256withRSA algorithm (RSASSA-PKCS1-V1_5-SIGN with SHA-256 hash function) in Windows, and I'm having some validation errors. I was wondering if anyone has experience trying to do it.
The summary of what I'm doing:
CryptAcquireContextW with MS_ENH_RSA_AES_PROV and PROV_RSA_AES
CryptImportKey after running CryptStringToBinary on a pem and applying to that CryptDecodeObjectEx with PKCS_RSA_PRIVATE_KEY
CryptCreateHash with CALG_SHA_256
CryptHashData and CryptSignHash using AT_KEYEXCHANGE to use the imported key (AT_SIGNATURE didn't work)
If you want the detailed version, it's here:
Create a CSP
WCHAR const container[] = L"ContainerName";
HCRYPTPROV provider;
CryptAcquireContextW(
&provider,
container,
MS_ENH_RSA_AES_PROV,
PROV_RSA_AES,
0));
Import my private key
char const key_string[] = "-----BEGIN RSA PRIVATE KEY-----"
"<base64 encoded key>"
"-----END RSA PRIVATE KEY-----";
DWORD key_bytes_size;
BYTE* key_bytes;
CryptStringToBinaryA(
key_string,
0,
CRYPT_STRING_BASE64HEADER,
nullptr, &key_bytes_size,
nullptr, nullptr);
key_bytes = new BYTE[key_bytes_size];
CryptStringToBinaryA(
key_string,
0,
CRYPT_STRING_BASE64HEADER,
key_bytes, &key_bytes_size,
nullptr, nullptr);
DWORD key_blob_size;
BYTE* key_blob;
CryptDecodeObjectEx(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
PKCS_RSA_PRIVATE_KEY,
key_bytes, key_bytes_size,
0, nullptr,
nullptr, &key_blob_size);
key_blob = new BYTE[key_blob_size];
CryptDecodeObjectEx(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
PKCS_RSA_PRIVATE_KEY,
key_bytes, key_bytes_size,
0, nullptr,
key_blob, &key_blob_size);
HCRYPTKEY key_handle;
CryptImportKey(
provider, // The one I created before
key_blob, key_blob_size,
nullptr, 0,
&key_handle);
(I hate that the API is so verbose, especially the two-step dance to ask for a length and then fill the buffer)
Once I've done this, I then hash and sign my data:
char const data[] = "Stuff to sign";
HCRYPTHASH hash_handle;
CryptCreateHash(provider, CALG_SHA_256, 0, 0, &hash_handle);
DWORD data_length = std::strlen(data);
CryptHashData(
hash_handle,
reinterpret_cast<const BYTE*>(data), data_length,
0);
DWORD signature_size;
BYTE* signature;
CryptSignHash(
hash_handle,
AT_KEYEXCHANGE, // AT_SIGNATURE didn't work
nullptr, 0,
nullptr, signature_size);
signature = new BYTE[signature_size];
CryptSignHash(
hash_handle,
AT_KEYEXCHANGE, // AT_SIGNATURE didn't work
nullptr, 0,
signature, signature_size);
Will this produce the kind of signature I want? Should I set different parameters for the AcquireContext call?
I'm trying to load a self-signed certificate generated on a different host and imported to my machine. Cert has been imported and shows up in mmc correctly. But CertFindCertificateInStore fails to load, when searched through SHA1 thumbprint.
Here's code:
const LPWSTR certThumbprint = L"2af92932d0164f52b20b1ccfdf0e1e4d525fbc08";
CryptStringToBinary(certThumbprint, SHA1_HASH_STRING_LENGTH, CRYPT_STRING_HEXRAW, NULL, &dwHashDataLength, NULL,NULL);
pHashData = new BYTE[dwHashDataLength];
CryptStringToBinary(certThumbprint,SHA1_HASH_STRING_LENGTH,CRYPT_STRING_HEXRAW,pHashData,&dwHashDataLength,NULL, NULL);
hashBlob.cbData = dwHashDataLength;
hashBlob.pbData = pHashData;
if ( !(pCertContext = CertFindCertificateInStore(
hSystemCertStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0,
CERT_FIND_SHA1_HASH,
&hashBlob,
NULL)))
{
printf("\n finding cert in system store failed: %d \n", GetLastError());
exit(-1);
}
I need to load a PEM encoded X.509 certificate into a Windows Crypto API context to use with C++. They are the ones that have -----BEGIN RSA XXX KEY----- and -----END RSA XXX KEY-----. I found examples for Python and .NET but they use specific functions I can't relate to the plain Windows Crypto API.
I understand how to encrypt/decrypt once I've got a HCRYPTKEY.
BUT, I just don't get how to import the Base64 blob in the .PEM file(s) and get a HCRYPTKEY that I can use out of it.
I have that strange feeling that there is more to it than simply calling CryptDecodeObject().
Any pointers that can put me on track? I've already lost two days doing "trial & error" programming and getting nowhere.
KJKHyperion said in his answer:
I discovered the "magic" sequence of calls to import a RSA public key in PEM format. Here you go:
decode the key into a binary blob with CryptStringToBinary; pass CRYPT_STRING_BASE64HEADER in dwFlags
decode the binary key blob into a CERT_PUBLIC_KEY_INFO with CryptDecodeObjectEx; pass X509_ASN_ENCODING in dwCertEncodingType and X509_PUBLIC_KEY_INFO in lpszStructType
decode the PublicKey blob from the CERT_PUBLIC_KEY_INFO into a RSA key blob with CryptDecodeObjectEx; pass X509_ASN_ENCODING in dwCertEncodingType and RSA_CSP_PUBLICKEYBLOB in lpszStructType
import the RSA key blob with CryptImportKey
This sequence really helped me understand what's going on, but it didn't work for me as-is. The second call to CryptDecodeObjectEx gave me an error:
"ASN.1 bad tag value met".
After many attempts at understanding Microsoft documentation, I finally realized that the output of the fist decode cannot be decoded as ASN again, and that it is actually ready for import. With this understanding I found the answer in the following link:
http://www.ms-news.net/f2748/problem-importing-public-key-4052577.html
Following is my own program that imports a public key from a .pem file to a CryptApi context:
int main()
{
char pemPubKey[2048];
int readLen;
char derPubKey[2048];
size_t derPubKeyLen = 2048;
CERT_PUBLIC_KEY_INFO *publicKeyInfo;
int publicKeyInfoLen;
HANDLE hFile;
HCRYPTPROV hProv = 0;
HCRYPTKEY hKey = 0;
/*
* Read the public key cert from the file
*/
hFile = CreateFileA( "c:\\pub.pem", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if ( hFile == INVALID_HANDLE_VALUE )
{
fprintf( stderr, "Failed to open file. error: %d\n", GetLastError() );
}
if ( !ReadFile( hFile, pemPubKey, 2048, &readLen, NULL ) )
{
fprintf( stderr, "Failed to read file. error: %d\n", GetLastError() );
}
/*
* Convert from PEM format to DER format - removes header and footer and decodes from base64
*/
if ( !CryptStringToBinaryA( pemPubKey, 0, CRYPT_STRING_BASE64HEADER, derPubKey, &derPubKeyLen, NULL, NULL ) )
{
fprintf( stderr, "CryptStringToBinary failed. Err: %d\n", GetLastError() );
}
/*
* Decode from DER format to CERT_PUBLIC_KEY_INFO
*/
if ( !CryptDecodeObjectEx( X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, derPubKey, derPubKeyLen,
CRYPT_ENCODE_ALLOC_FLAG, NULL, &publicKeyInfo, &publicKeyInfoLen ) )
{
fprintf( stderr, "CryptDecodeObjectEx 1 failed. Err: %p\n", GetLastError() );
return -1;
}
/*
* Acquire context
*/
if( !CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) )
{
{
printf( "CryptAcquireContext failed - err=0x%x.\n", GetLastError() );
return -1;
}
}
/*
* Import the public key using the context
*/
if ( !CryptImportPublicKeyInfo( hProv, X509_ASN_ENCODING, publicKeyInfo, &hKey ) )
{
fprintf( stderr, "CryptImportPublicKeyInfo failed. error: %d\n", GetLastError() );
return -1;
}
LocalFree( publicKeyInfo );
/*
* Now use hKey to encrypt whatever you need.
*/
return 0;
}
I discovered the "magic" sequence of calls to import a RSA public key in PEM format. Here you go:
decode the key into a binary blob with CryptStringToBinary; pass CRYPT_STRING_BASE64HEADER in dwFlags
decode the binary key blob into a CERT_PUBLIC_KEY_INFO with CryptDecodeObjectEx; pass X509_ASN_ENCODING in dwCertEncodingType and X509_PUBLIC_KEY_INFO in lpszStructType
decode the PublicKey blob from the CERT_PUBLIC_KEY_INFO into a RSA key blob with CryptDecodeObjectEx; pass X509_ASN_ENCODING in dwCertEncodingType and RSA_CSP_PUBLICKEYBLOB in lpszStructType
import the RSA key blob with CryptImportKey
I'm currently facing the same difficulty. I haven't finished coding a solution but as I understand it you need to strip off the ----- BEGIN etc ----- and ----- END etc ------ tags and decode the Base64.
This leaves you with a DER encoded string, which you need to parse to get the modulus and public exponent. From those you can populate the PUBLICKEYSTRUC and RSAPUBKEY structures. Good luck ;-)