Sign certificate request with smartcard private key in PKCS#11 - c++

I would like to sign a certificate request with a smartcard's private key with PKCS#11. I do mean signing the CSR itself and not writing a certificate in response.
So I am using OpenSSL to create the X509 certificate request, and I want to use PKCS#11 and C_Sign to create the signature. How can I do that?
This is what I have currently, but when I try to generate a certificate from this request with OpenSSL CLI, it says the signature doesn't match so I must be doing something wrong. I'm not sure what to pass C_Sign —right now I've tried the output of i2d_X509_REQ()— and how to set the signature back in X509_REQ once it's been created (I've tried building an ASN1_BIT_STRING object).
Note: this is not a duplicate of this question because this one is for a certificate and works for an old version of OpenSSL's API. Although, I have tried to use the answer by manually exposing the internals of the X509_REQ structure (see last code block).
X509_REQ makeCSR() {
/* Create OpenSSL EVP_PKEY from exported public key components */
RSA* openssl_rsa = RSA_new();
BIGNUM* bn_modulus = BN_bin2bn(modulus.data(), (int) modulus.size(), nullptr);
BIGNUM* bn_public_exponent = BN_bin2bn(public_exponent.data(), (int) public_exponent.size(), nullptr);
int success = RSA_set0_key(openssl_rsa, bn_modulus, bn_public_exponent, nullptr);
EVP_PKEY* evp_pkey = EVP_PKEY_new();
/* Add public key to certificate request */
EVP_PKEY_assign(evp_pkey, EVP_PKEY_RSA, openssl_rsa);
X509_REQ* request = X509_REQ_new();
X509_REQ_set_pubkey(request, evp_pkey);
/* Set certificate request attributes */
// ...
/* Sign certificate request with smart card */
unsigned char* buffer { nullptr };
int size = i2d_X509_REQ(request, &buffer);
std::vector<unsigned char> der_encoded_request(buffer, buffer + size);
std::vector<unsigned char> signature = smartcard->signCertificateRequest(der_encoded_request);
/* Build signature object */
ASN1_BIT_STRING* asn1_signature = ASN1_BIT_STRING_new();
ASN1_BIT_STRING_set(asn1_signature, signature.data(), (int) signature.size());
asn1_signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
asn1_signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
X509_ALGOR* x509_algor = X509_ALGOR_new();
ASN1_OBJECT* a = OBJ_nid2obj(pkcs11SignatureAlgorithmToNid(CKM_SHA1_RSA_PKCS));
X509_ALGOR_set0(x509_algor, a, V_ASN1_NULL, nullptr);
/* Add signature to X509_REQ */
X509_REQ_set1_signature_algo(request, x509_algor);
X509_REQ_set0_signature(request, asn1_signature);
return request;
}
std::vector<unsigned char> signCertificateRequest(std::vector<unsigned char>& certificate_request)
{
CK_MECHANISM mechanism = { CKM_SHA1_RSA_PKCS, nullptr, 0 };
auto result = s_pkcs11->fn->C_SignInit(m_session_handle, &mechanism, private_key_handle);
unsigned long signature_length { 0 };
result = pkcs11->C_Sign(m_session_handle,
certificate_request.data(),
(unsigned long) certificate_request.size(),
nullptr,
&signature_length);
std::vector<unsigned char> signature(signature_length);
result = pkcs11->C_Sign(m_session_handle,
certificate_request.data(),
(unsigned long) certificate_request.size(),
signature.data(),
&signature_length);
return signature;
}
I've also tried exposing the internals of X509_REQ and passing the output of i2d_X509_REQ_INFO(&request->req_info, &buffer) to C_Sign; or using ASN1_item_i2d(); and also copying the signature output directly to request->signature->data.
request->req_info.enc.modified = 1;
X509_ALGOR_set0(&request->sig_alg,
OBJ_nid2obj(pkcs11SignatureAlgorithmToNid(CKM_SHA1_RSA_PKCS)),
V_ASN1_NULL,
nullptr);
unsigned char* certDerBuf = NULL;
const auto certDerLen = ASN1_item_i2d(ASN1_VALUE*) &request->req_info,
&certDerBuf,
ASN1_ITEM_rptr(X509_REQ_INFO));
std::vector<unsigned char> certDerVec(certDerBuf, certDerBuf + certDerLen);
std::vector<unsigned char> signature = smartcard->signCertificateRequest(certDerVec);
request->signature->data = (unsigned char*) OPENSSL_malloc(signature.size());
request->signature->length = (int) signature.size();
request->signature->data = signature.data();

I was able to resolve my problem in the end; this is my complete solution. Error handling and PKCS#11 session handling have been omitted. I use a unique_ptr for managing OpenSSL resources. I had to expose OpenSSL's X509_REQ and X509_REQ_INFO structures because I couldn't find accessors for req_info in OpenSSL 1.1.1h.
// OpenSSL internal definitions
using CRYPTO_REF_COUNT = int;
struct X509_req_info_st {
ASN1_ENCODING enc; /* cached encoding of signed part */
ASN1_INTEGER* version; /* version, defaults to v1(0) so can be NULL */
X509_NAME* subject; /* certificate request DN */
X509_PUBKEY* pubkey; /* public key of request */
/*
* Zero or more attributes.
* NB: although attributes is a mandatory field some broken
* encodings omit it so this may be NULL in that case.
*/
STACK_OF(X509_ATTRIBUTE) * attributes;
};
struct X509_req_st {
X509_REQ_INFO req_info; /* signed certificate request data */
X509_ALGOR sig_alg; /* signature algorithm */
ASN1_BIT_STRING* signature; /* signature */
CRYPTO_REF_COUNT references;
CRYPTO_RWLOCK* lock;
};
template<typename T>
using AutoDeletedPtr = std::unique_ptr<T, std::function<void(T*)>>;
void makeCSR(const std::vector<unsigned char>& modulus,
const std::vector<unsigned char>& public_exponent)
{
/* Create OpenSSL EVP_PKEY from exported public key components */
auto* openssl_rsa = RSA_new();
auto bn_modulus = BN_bin2bn(modulus.data(), static_cast<int>(modulus.size()), nullptr);
auto bn_public_exponent = BN_bin2bn(public_exponent.data(),
static_cast<int>(public_exponent.size()),
nullptr);
auto success = RSA_set0_key(openssl_rsa, bn_modulus, bn_public_exponent, nullptr);
auto key_file = AutoDeletedPtr<BIO>(BIO_new_file("key.pem", "wb"), BIO_free);
PEM_write_bio_RSA_PUBKEY(key_file.get(), openssl_rsa);
auto evp_pkey = AutoDeletedPtr<EVP_PKEY>(EVP_PKEY_new(), EVP_PKEY_free);
/* Add public key to certificate request */
EVP_PKEY_assign(evp_pkey.get(), EVP_PKEY_RSA, openssl_rsa);
auto request = AutoDeletedPtr<X509_REQ>(X509_REQ_new(), X509_REQ_free);
X509_REQ_set_pubkey(request.get(), evp_pkey.get());
/* Set certificate request attributes */
// ...
/* Sign certificate request with smart card */
unsigned char* buffer { nullptr };
auto size = i2d_X509_REQ_INFO(&request->req_info, &buffer);
std::vector<unsigned char> der_encoded_request(buffer, buffer + size);
auto signature = signCertificateRequest(der_encoded_request);
auto asn1_signature = ASN1_BIT_STRING_new();
ASN1_BIT_STRING_set(asn1_signature, signature.data(), static_cast<int>(signature.size()));
auto x509_algor = AutoDeletedPtr<X509_ALGOR>(X509_ALGOR_new(), X509_ALGOR_free);
auto* a = OBJ_nid2obj(pkcs11SignatureAlgorithmToNid(CKM_SHA1_RSA_PKCS));
auto algor_set_result = X509_ALGOR_set0(x509_algor.get(), a, V_ASN1_NULL, nullptr);
X509_REQ_set1_signature_algo(request.get(), x509_algor.get());
X509_REQ_set0_signature(request.get(), asn1_signature);
request->signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
request->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
// Do what you want with the request
}
std::vector<unsigned char> signCertificateRequest(std::vector<unsigned char>& certificate_request)
{
// You'll need an open session and be logged in
CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY;
CK_KEY_TYPE private_key_type = CKK_RSA;
std::array<unsigned char, 4> id = "myid";
std::vector<CK_ATTRIBUTE> private_key_template = {
{ CKA_CLASS, &private_key_class, sizeof(private_key_class) },
{ CKA_KEY_TYPE, &private_key_type, sizeof(private_key_type) },
{ CKA_ID, id.data(), static_cast<long>(id.size()) },
};
auto result = s_pkcs11->fn->C_FindObjectsInit(m_session_handle,
private_key_template.data(),
static_cast<unsigned long>(private_key_template.size()));
CK_OBJECT_HANDLE private_key_handle { 0 };
unsigned long object_count { 0 };
result = s_pkcs11->fn->C_FindObjects(m_session_handle, &private_key_handle, 1, &object_count);
result = s_pkcs11->fn->C_FindObjectsFinal(m_session_handle);
CK_MECHANISM mechanism = { CKM_SHA1_RSA_PKCS, nullptr, 0 };
auto result = s_pkcs11->fn->C_SignInit(m_session_handle, &mechanism, private_key_handle);
unsigned long signature_length { 0 };
result = s_pkcs11->fn->C_Sign(m_session_handle,
certificate_request.data(),
static_cast<unsigned long>(certificate_request.size()),
nullptr,
&signature_length);
std::vector<unsigned char> signature(signature_length);
result = s_pkcs11->fn->C_Sign(m_session_handle,
certificate_request.data(),
static_cast<unsigned long>(certificate_request.size()),
signature.data(),
&signature_length);
return signature;
}

Related

PKCS#7 signing Base64 string and add signer info openssl

There is problem found while signing Nonce(Base64 string) with PKCS#7 using openssl
the problem is when i decode the signature the nonce is trimmed (get 4 char and the expected is 8 char)
Here is the code.
int main(int argc, char *argv[])
{
QString nonce = "Jd0VAO74";
QDateTime dateTime = QDateTime::fromString("2022-12-15T13:51:46Z", Qt::ISODateWithMs);
unsigned char*signature = signNonce(nonce, dateTime);
qDebug() << signature;
return 0;
}
unsigned char* signNonce(nonce, dateTime){
QContentInfo contentInfo = QContentInfo(QByteArray::fromBase64(nonce.toLatin1()));
auto signedCms = QSignedCms(contentInfo);
QOpenssl::QOpensslCertificate qOpensslCertificate(getCertificate());
QCmsSigner cmsSigner = QCmsSigner(qOpensslCertificate);
cmsSigner.setDigestType(QOpenssl::DigestType::SHA256);
cmsSigner.setPkcs9SigningTime(serverDateTime);
signedCms.computeSignatureNew(cmsSigner);
auto l_pSignedCms = PKCS7_PTR(PKCS7_new(),::PKCS7_free);
// set certificate and private key in a signer info.
QSignerInfo qsignerInfo;
PKCS7_SIGNER_INFO* signerInfo = PKCS7_SIGNER_INFO_new();
X509_PTR pX509 = cmsSigner.getCertificate().getCertificate();
EVP_PKEY_PTR pKey = cmsSigner.getCertificate().getPrivateKey();
const EVP_MD* pMD = EVP_sha256();
PKCS7_SIGNER_INFO_set(signerInfo, pX509.get(), pKey.get(), pMD);
// set signing time attribute.
ASN1_TIME* pSigningTime = ASN1_TIME_set(nullptr, cmsSigner.getPkcs9SigningTime().toTime_t());
PKCS7_add0_attrib_signing_time(signerInfo, pSigningTime);
qsignerInfo.setPkcs9SigningTime(cmsSigner.getPkcs9SigningTime());
// set message digest attribute.
QCryptographicHash::Algorithm algo = cmsSigner.getDigestType() == DigestType::SHA256
? QCryptographicHash::Algorithm::Sha256
: QCryptographicHash::Algorithm::Sha1;
QByteArray hash = QCryptographicHash::hash(m_ContentInfo.getContent(), algo);
const auto* pHash = reinterpret_cast<const unsigned char*>(hash.constData());
PKCS7_add1_attrib_digest(signerInfo, pHash, m_ContentInfo.getContent().length());
qsignerInfo.setDigestType(cmsSigner.getDigestType());
qsignerInfo.setHash(hash);
// set content type attribute.
PKCS7_add_attrib_content_type(signerInfo, OBJ_nid2obj(NID_pkcs7_data));
// sign signerinfo.
if(PKCS7_SIGNER_INFO_sign(signerInfo) <= 0) {
qCritical() << ERR_error_string(ERR_get_error(), nullptr);
return;
}
// add signer info to cms.
PKCS7_add_signer(l_pSignedCms.get(), signerInfo);
// set data to cms.
// set certificate to cms.
PKCS7_add_certificate(l_pSignedCms.get(), pX509.get());
// set certificate chain
for(const QOpensslCertificate& cert : cmsSigner.getCertificate().getCertificateChain()) {
if(!cert.isSelfSigned())
PKCS7_add_certificate(l_pSignedCms.get(), cert.getCertificate().get());
}
// set content data.
BIO_PTR pContent = BIO_PTR(BIO_new(BIO_s_mem()), ::BIO_free);
BIO_puts(pContent.get(), m_ContentInfo.getContent().constData());
m_pSignedCms = PKCS7_PTR(
PKCS7_sign(pX509.get(), pKey.get(), nullptr, pContent.get(), 0),
::PKCS7_free);
unsigned char* pSignedValue = nullptr;
int result = i2d_PKCS7(m_pSignedCms.get(), &pSignedValue);
return pSignedValue ;
}
after decodeing online decoder the signature, we found nonce in hex 0x25 DD 15
the nonce only contains 4 char Jd0V
any one has a clue ?
I try to figure out why the decoded signature only contains 4 char not 8

How to generate and hex encode a ED25519 keypair using openssl 3.0 c++

So far I can do the following. But I have no idea if it is working because I can't see the keys. I am planning on extracting them into a char array and storing them In a struct. So I need the full encoded byte array for the private and public keys.
I think I have it working partially for RSA. But I can't figure out how to use ED22519.
static bool GenerateEncryptionKeys(ofstream *file)
{
EVP_PKEY_CTX *ctx;
EVP_PKEY *pkey = NULL;
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
if (ctx)
{
if (EVP_PKEY_keygen_init(ctx) > 0)
{
if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048) > 0)
{
if (EVP_PKEY_keygen(ctx, &pkey) > 0)
{
print("All good");
}
}
}
}
return.
Nevermind. I solved it.
static bool GenerateEncryptionKeys()
{
EVP_PKEY *my_pkey = nullptr;
EVP_PKEY_CTX *my_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, nullptr);
EVP_PKEY_keygen_init(my_ctx);
EVP_PKEY_keygen(my_ctx, &my_pkey);
std::size_t pub_key_allocated_length;
EVP_PKEY_get_raw_public_key(my_pkey, NULL, &pub_key_allocated_length);
unsigned char *public_key = new unsigned char[pub_key_allocated_length];
EVP_PKEY_get_raw_public_key(my_pkey, public_key, &pub_key_allocated_length);
std::size_t pkey_allocated_length;
EVP_PKEY_get_raw_private_key(my_pkey, NULL, &pkey_allocated_length);
unsigned char *private_key = new unsigned char[pkey_allocated_length];
EVP_PKEY_get_raw_private_key(my_pkey, private_key, &pkey_allocated_length);
WriteEncryptionKeys(convertToString((const char *)private_key, pkey_allocated_length), convertToString((const char *)public_key, pub_key_allocated_length));
return true;
}

How to find private key in token by using X509 certificate modulus in C++

In c++ code using pkcs#11 we are trying to find the private key and install corresponding x509 certificate in the token. But unable to find the key pair in token using modulus. Below is my code sample.
//Install certificate
const char bytes[] = "-----BEGIN CERTIFICATE-----" "\n"
....
"-----END CERTIFICATE-----" "\n";
BIO *bio_mem = BIO_new(BIO_s_mem());
BIO_puts(bio_mem, bytes);
X509 * x509 = PEM_read_bio_X509(bio_mem, NULL, NULL, NULL);
//
BIO *bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);
EVP_PKEY *pkey = X509_get_pubkey(x509);
RSA *rsa_key;
DSA *dsa_key;
char *rsa_e_dec, *rsa_n_hex, *dsa_p_hex,
*dsa_q_hex, *dsa_g_hex, *dsa_y_hex;
rsa_key = pkey->pkey.rsa;
//IFNULL_FAIL(rsa_e_dec, "unable to extract rsa exponent");
CK_BYTE_PTR modulus, exponent;
modulus = (unsigned char *)malloc(256);
int mo = BN_bn2bin(rsa_key->n, modulus);
//EVP_PKEY_free(pkey);
// CK_RV result;
CK_OBJECT_HANDLE hObject;
CK_OBJECT_HANDLE hObjects[100];
CK_OBJECT_HANDLE_PTR hObject_PTR = NULL;
CK_ULONG count;
vector<CK_OBJECT_HANDLE> *handles = new vector<CK_OBJECT_HANDLE>();
//Object class attribute
CK_OBJECT_CLASS classValue = CKO_PRIVATE_KEY;
CK_OBJECT_CLASS keytype = CKK_RSA;
CK_ATTRIBUTE privKeySearchTemplate[] = {
{ CKA_CLASS, &classValue,sizeof(classValue) },
{ CKA_KEY_TYPE, &keytype,sizeof(keytype) },
{ CKA_MODULUS, &modulus, sizeof(modulus) },
};
//
//{ CKA_PUBLIC_EXPONENT, exponent},
// Read label and ID from private key handle
CK_ATTRIBUTE privKeyAttrsToRead[] =
{ { CKA_LABEL, NULL_PTR, 0 },
{ CKA_ID, NULL_PTR, 0 },
};
//WriteToLog(modulus, modulus_len11);
// Find all objects with the template specified
result = m_pPKCS11->C_FindObjectsInit(m_SessionHandle, privKeySearchTemplate, 2);
do {
// Find the next object
result = m_pPKCS11->C_FindObjects(m_SessionHandle, &hObject, 1, &count);
if (count != 0)
handles->push_back(hObject);
} while (count != 0);
result = m_pPKCS11->C_FindObjectsFinal(m_SessionHandle);
There are several bugs here:
{ CKA_MODULUS, &modulus, sizeof(modulus) }
like always, sizeof(modulus) is size of your pointer which is 4 or 8 based on your system. This should be size of your modulus which in your case is mo. In addition, use correct type here:
CK_KEY_TYPE keytype = CKK_RSA;
Another bug is here:
m_pPKCS11->C_FindObjectsInit(m_SessionHandle, privKeySearchTemplate, 2);
You are searching a template with 3 attributes, but you have set number of attributes as 2. Normally you need to write code like this to prevent such bugs:
m_pPKCS11->C_FindObjectsInit(m_SessionHandle, privKeySearchTemplate, sizeof(privKeySearchTemplate) / sizeof(CK_ATTRIBUTE));
Finally, you need to allocate enough memory for your modulus before using BN_bn2bin, unless you like to get memory exceptions. Allocating 256 bytes may not be sufficient.

Unable to do RSA Encryption/Decryption using Crypto++ (isValidCoding is false)

I am using Crypto++ to encrypt an array of bytes using RSA. I have followed Crypto++ wiki's samples with no luck getting them to work. Encryption and Decryption in all the samples are done within a single process but I am trying to decrypt the content which is already encrypted in another process.
Here is my code:
class FixedRNG : public CryptoPP::RandomNumberGenerator
{
public:
FixedRNG(CryptoPP::BufferedTransformation &source) : m_source(source) {}
void GenerateBlock(byte *output, size_t size)
{
m_source.Get(output, size);
}
private:
CryptoPP::BufferedTransformation &m_source;
};
uint16_t Encrypt()
{
byte *oaepSeed = new byte[2048];
for (int i = 0; i < 2048; i++)
{
oaepSeed[i] = (byte)i;
}
CryptoPP::ByteQueue bq;
bq.Put(oaepSeed, 2048);
FixedRNG prng(bq);
Integer n("Value of N"),
e("11H"),
d("Value of D");
RSA::PrivateKey privKey;
privKey.Initialize(n, e, d);
RSA::PublicKey pubKey(privKey);
CryptoPP::RSAES_OAEP_SHA_Encryptor encryptor( pubKey );
assert( 0 != encryptor.FixedMaxPlaintextLength() );
byte blockSize = encryptor.FixedMaxPlaintextLength();
int divisionCount = fileSize / blockSize;
int proccessedBytes = 0;
// Create cipher text space
uint16_t cipherSize = encryptor.CiphertextLength( blockSize );
assert( 0 != cipherSize );
encryptor.Encrypt(prng, (byte*)plaintext, blockSize, (byte*)output);
return cipherSize;
}
void Decrypt(uint16_t cipherSize)
{
byte *oaepSeed = new byte[2048];
for (int i = 0; i < 2048; i++)
{
oaepSeed[i] = (byte)i;
}
CryptoPP::ByteQueue bq;
bq.Put(oaepSeed, 2048);
FixedRNG prng(bq);
Integer n("Value of N"),
e("11H"),
d("Value of D");
RSA::PrivateKey privKey;
privKey.Initialize(n, e, d);
//RSA::PublicKey pubKey(privKey);
CryptoPP::RSAES_OAEP_SHA_Decryptor decryptor( privKey );
byte blockSize = decryptor.FixedMaxPlaintextLength();
assert(blockSize != 0);
size_t maxPlainTextSize = decryptor.MaxPlaintextLength( cipherSize );
assert( 0 != maxPlainTextSize );
void* subBuffer = malloc(maxPlainTextSize);
CryptoPP::DecodingResult result = decryptor.Decrypt(prng, (byte*)cipherText, cipherSize, (byte*)subBuffer);
assert( result.isValidCoding );
assert( result.messageLength <= maxPlainTextSize );
}
Unfortunately, value of isValidCoding is false. I think I am misunderstanding something about RSA encryption/decryption!!
Note that, privKey and pubKey have been validated using KEY.Validate(prng, 3).
I have also tried to use RAW RSA instead of OAEP and SHA with no luck. I have tried to debug through crypto++ code, what I am suspicious about is prng variable. I think there is something wrong with it. I have also used AutoSeededRandomPool instead of FixedRNG but it didn't help. Worth to know that, if I copy the decryption code right after encryption code and execute it in Encrypt() method, everything is fine and isValidCoding is true!!
This is probably not be correct:
byte blockSize = encryptor.FixedMaxPlaintextLength();
...
encryptor.Encrypt(prng, (byte*)plaintext, blockSize, (byte*)output);
return cipherSize;
Try:
size_t maxLength = encryptor.FixedMaxPlaintextLength();
size_t cipherLength = encryptor.CiphertextLength( blockSize );
...
SecureByteBlock secBlock(cipherLength);
cipherLength = encryptor.Encrypt(prng, (byte*)plaintext, blockSize, secBlock);
secBlock.resize(cipherLength);
FixedMaxPlaintextLength returns a size_t, not a byte.
You should probably be calling CiphertextLength on plaintext.
I'm not really sure how you are just returning an uint_t from encrypt().
You might do better by starting fresh, and using an example from the Crypto++ as a starting point. I'm not sure this design is worth pursuing.
If you start over, then Shoup's Elliptic Curve Integrated Encryption Scheme (ECIES) would be a good choice since it combines public key with symmetric ciphers and authentication tags.

How to encrypt and decrypt a const char* in WinRT

I have been trying to write encrypt and decrypt functions whose signatures require the input and the output strings to be void* type only. The code works fine if the inputs can be specified as IBuffer^ but in the other case the source string and the encrypted->decrypted string do not match.
CodeIBuffer^ byteArrayToIBufferPtr(byte *source, int size)
{
Platform::ArrayReference<uint8> blobArray(source, size);
IBuffer ^buffer = CryptographicBuffer::CreateFromByteArray(blobArray);
return buffer;
}
byte* IBufferPtrToByteArray(IBuffer ^buffer)
{
Array<unsigned char,1U> ^platArray = ref new Array<unsigned char,1U>(256);
CryptographicBuffer::CopyToByteArray(buffer,&platArray);
byte *dest = platArray->Data;
return dest;
}
int DataEncryption::encryptData(EncryptionAlgorithm algo, int keySize, void* srcData, const unsigned int srcSize,
void*& encData, unsigned int& encSize)
{
LOG_D(TAG, "encryptData()");
if(srcData == nullptr)
{
LOG_E(TAG,"");
return DataEncryption::RESULT_EMPTY_DATA_ERROR;
}
if(srcSize == 0)
{
LOG_E(TAG,"");
return DataEncryption::RESULT_SIZE_ZERO_ERROR;
}
IBuffer^ encrypted;
IBuffer^ buffer;
IBuffer^ iv = nullptr;
String^ algName;
bool cbc = false;
switch (algo)
{
case DataEncryption::ENC_DEFAULT:
algName = "AES_CBC";
cbc = true;
break;
default:
break;
}
// Open the algorithm provider for the algorithm specified on input.
SymmetricKeyAlgorithmProvider^ Algorithm = SymmetricKeyAlgorithmProvider::OpenAlgorithm(algName);
// Generate a symmetric key.
IBuffer^ keymaterial = CryptographicBuffer::GenerateRandom((keySize + 7) / 8);
CryptographicKey^ key;
try
{
key = Algorithm->CreateSymmetricKey(keymaterial);
}
catch(InvalidArgumentException^ e)
{
LOG_E(TAG,"encryptData(): Could not create key.");
return DataEncryption::RESULT_ERROR;
}
// CBC mode needs Initialization vector, here just random data.
// IV property will be set on "Encrypted".
if (cbc)
iv = CryptographicBuffer::GenerateRandom(Algorithm->BlockLength);
// Set the data to encrypt.
IBuffer ^srcDataBuffer = byteArrayToIBufferPtr(static_cast<byte*>(srcData),256);
// Encrypt and create an authenticated tag.
encrypted = CryptographicEngine::Encrypt(key, srcDataBuffer, iv);
//encData = encrypted;
byte *bb = IBufferPtrToByteArray(encrypted);
encData = IBufferPtrToByteArray(encrypted);
encSize = encrypted->Length;
return DataEncryption::RESULT_SUCCESS;
}
int DataEncryption::decryptData(EncryptionAlgorithm algo, int keySize, void* encData, const unsigned int encSize,
void*& decData, unsigned int& decSize)
{
LOG_D(TAG, "decryptData()");
if(encData == nullptr)
{
LOG_E(TAG,"");
return DataEncryption::RESULT_EMPTY_DATA_ERROR;
}
if(encSize == 0)
{
LOG_E(TAG,"");
return DataEncryption::RESULT_SIZE_ZERO_ERROR;
}
IBuffer^ encrypted;
IBuffer^ decrypted;
IBuffer^ iv = nullptr;
String^ algName;
bool cbc = false;
switch (algo)
{
case DataEncryption::ENC_DEFAULT:
algName = "AES_CBC";
cbc = true;
break;
default:
break;
}
// Open the algorithm provider for the algorithm specified on input.
SymmetricKeyAlgorithmProvider^ Algorithm = SymmetricKeyAlgorithmProvider::OpenAlgorithm(algName);
// Generate a symmetric key.
IBuffer^ keymaterial = CryptographicBuffer::GenerateRandom((keySize + 7) / 8);
CryptographicKey^ key;
try
{
key = Algorithm->CreateSymmetricKey(keymaterial);
}
catch(InvalidArgumentException^ e)
{
LOG_E(TAG,"encryptData(): Could not create key.");
return DataEncryption::RESULT_ERROR;
}
// CBC mode needs Initialization vector, here just random data.
// IV property will be set on "Encrypted".
if (cbc)
iv = CryptographicBuffer::GenerateRandom(Algorithm->BlockLength);
// Set the data to decrypt.
byte *cc = static_cast<byte*>(encData);
IBuffer ^encDataBuffer = byteArrayToIBufferPtr(cc,256);
// Decrypt and verify the authenticated tag.
decrypted = CryptographicEngine::Decrypt(key, encDataBuffer, iv);
byte *bb = IBufferPtrToByteArray(decrypted);
decData = IBufferPtrToByteArray(decrypted);
decSize = decrypted->Length;
return DataEncryption::RESULT_SUCCESS;
}
I'm guessing that the problem is with this function:
byte* IBufferPtrToByteArray(IBuffer ^buffer)
{
Array<unsigned char,1U> ^platArray = ref new Array<unsigned char,1U>(256);
CryptographicBuffer::CopyToByteArray(buffer,&platArray);
byte *dest = platArray->Data;
return dest;
}
What you're doing there is allocating a new Platform::Array<byte>^ with 1 reference, then getting a pointer to its internally-managed storage, then returning that pointer-- at which point the Array is being dereferenced and is thus deallocating its underlying storage. Thus the pointer you return refers to freed memory. The next allocation is likely to overwrite those bytes.
What you'll need to do is take the return-by-reference Array<byte>^ from CopyToByteArray() (which creates a new Array, presumably wrapping the bytes of the input IBuffer^, and returns it) and copy that array's contents.
Your end result will function similarly to this snippet from the Readium SDK project, which takes a std::string instance, hashes it using SHA-1, and copies the hash data into a member variable uint8_t _key[KeySize]:
using namespace ::Platform;
using namespace ::Windows::Foundation::Cryptography;
using namespace ::Windows::Foundation::Cryptography::Core;
auto byteArray = ArrayReference<byte>(reinterpret_cast<byte*>(const_cast<char*>(str.data())), str.length());
auto inBuf = CryptographicBuffer::CreateFromByteArray(byteArray);
auto keyBuf = HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha1)->HashData(inBuf);
Array<byte>^ outArray = nullptr;
CryptographicBuffer::CopyToByteArray(keyBuf, &outArray);
memcpy_s(_key, KeySize, outArray->Data, outArray->Length);
The steps:
Create an ArrayReference<byte> corresponding to the bytes in the std::string (no copying).
Pass that to CryptographicBuffer::CreateFromByteArray() to get your IBuffer^. Still no copying of data.
Call your hash/encryption function, passing the IBuffer^ you just made. You get another IBuffer^ in return, which may or may not be using the exact same storage (that's really up to the implementation of the algorithm, I think).
Create a variable of type Array<byte>^. Don't allocate an object, you're going to be given one by reference.
Pass the address of that object into CryptographicBuffer::CopyToByteArray() to receive a copy of your key data.
While that Array^ remains valid, copy its bytes into your native array.