HMAC verification goes wrong with Openssl EVP - encryption

The code i wrote somehow does not work properly.
When using the same key and cipher with an online tool, the hmac is different.
My code is:
EVP_MD_CTX* mdctx = NULL;
const EVP_MD* md = NULL;
EVP_PKEY *pkey = NULL;
unsigned char md_value[EVP_MAX_MD_SIZE];
size_t md_len = 0;
mdctx = EVP_MD_CTX_create();
md = EVP_get_digestbyname("SHA256");
const unsigned char a[] = "qwertzuiopqwertzuiopqw";
pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, a, strlen("qwertzuiopqwertzuiopqw"));
EVP_DigestSignInit(mdctx, NULL, md, NULL, pkey);
EVP_DigestSignUpdate(mdctx, "Hallo", sizeof("Hallo")); // iv
EVP_DigestSignFinal(mdctx, md_value, &md_len);
The hmac calculated by my code is : eadfb51c9fb3f3fcd5741006861d04bc0d695347db4bfb9e04e954e17583c3c5.
The hmac calculated by the online-tool is: a9e0f9acc1452bdb796556c3c64e29d7f7ab47a59a179ec918c61894eefeba26.
What am i doing wrong?

Related

Implement PBEWithHmacSHA256AndAES_256 with openSSL 1.1.1 and/or Windows crypt functions

I have an implementation of password based encryption in Java. It formerly used PBEWithMD5AndDES and was later changed to use PBEWithHmacSHA256AndAES_256. The significant code looks like this:
//String cipherAlgorithm = "PBEWithMD5AndDES";
String cipherAlgorithm = "PBEWithHmacSHA256AndAES_256";
PBEKeySpec pbeKeySpec = new PBEKeySpec(passPhrase, salt, iterationCount);
SecretKey secretKey = SecretKeyFactory.getInstance(cipherAlgorithm).generateSecret(pbeKeySpec);
AlgorithmParameterSpec paramSpec = iv == null ?
new PBEParameterSpec(data.salt, iterationCount) :
new PBEParameterSpec(data.salt, iterationCount, new IvParameterSpec(iv));
cipher = Cipher.getInstance(cipherAlgorithm);
if (encrypt) {
cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec);
} else {
cipher.init(Cipher.DECRYPT_MODE, secretKey, paramSpec);
}
...
byte[] enc = cipher.doFinal(utf8);
I have also an implementation with openSSL (and with Windows crypt functions) for the old algorithm. Now I try to extend this to the new algorithm.
The old code using the openSSL implementation looks like this:
unsigned char iv[8];
unsigned char key[8];
memset(iv, '\0', sizeof(iv));
memset(key, '\0', sizeof(key));
int rc = EVP_BytesToKey(EVP_des_cbc(), EVP_md5(),
salt,
apasswd, alen, iterationCount,
key, iv);
if (rc != sizeof(key))
throw ...
EVP_CIPHER_CTX *ectx;
ectx = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_init(ectx);
if (!EVP_CipherInit_ex(ectx, EVP_des_cbc(), NULL, key, iv, encrypt ? 1 : 0))
throw ...
if (!EVP_CipherUpdate(ectx, ebuf, &ebuflen, (unsigned char *)abuf, abuflen))
throw ...
if (ebuflen > (int)abufsize)
throw ...
...
if (!EVP_CipherFinal_ex(ectx, ebuf, &ebuflen))
throw ...
...
EVP_CIPHER_CTX_free(ectx);
I was not successful to get this working. I tried already the following:
Replace EVP_des_cbc() with EVP_aes_256_cbc() and EVP_md5() : EVP_sha256()
Use PKCS5_PBKDF2_HMAC instead of EVP_BytesToKey (with iv and key sizes adapted to 16 and32)
PKCS5_PBKDF2_HMAC(apasswd, alen, salt, sizeof(salt),
iterationCount, EVP_sha256(),
sizeof(key), key);
But the decryption of data encrypted with the Java implementation fails (EVP_CipherFinal_ex returns an error, so the key seems wrong).
Unfortunately I did not find any information about how PBEWithHmacSHA256AndAES_256 is implemented in Java.
Could anyone point me to more information about this or has anyone successfully implemented similar things (with openSSL or Windows API) and have some hints about this?
I finally got it working, so I can answer the question myself now.
For openSSL, the key can be generated from the passphrase with:
int rc = PKCS5_PBKDF2_HMAC((const char *)passPhrase, pLen, salt,
sizeof(salt), iterationCount, EVP_sha256(),
sizeof(key), key);
For Windows, the key can be generated with:
NTSTATUS status;
BYTE key[32];
BCRYPT_ALG_HANDLE handle;
status = BCryptOpenAlgorithmProvider(&handle,
BCRYPT_SHA256_ALGORITHM, NULL,
BCRYPT_ALG_HANDLE_HMAC_FLAG);
status = BCryptDeriveKeyPBKDF2(handle, (BYTE*)passPhrase, sizeof(passPhrase),
salt, sizeof(salt), iterationCount, key, 32, 0);

decrypt AES input on Flutter, when on web use cryptoJS AES

On web, I'm using CryptoJS for decrypto JS:
CryptoJS.AES.decrypt(inputBase64, key).toString(CryptoJS.enc.Utf8);
Example:
input: "tzfwnxVwE/qNoaWRRfqLp11ZyhB4UtKO+0/Lvv5B7eE="
key: "20190225165436_15230006321670000_15510884759030000"
On flutter I can't find any library to decrypt with a key of any length.
I know "For AES, NIST selected three members of the Rijndael family, each with a block size of 128 bits, but three different key lengths: 128, 192 and 256 bits.
"
But I don't know how to convert any length key to 128 bit format?
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:tuple/tuple.dart';
import 'package:encrypt/encrypt.dart' as encrypt;
String encryptAESCryptoJS(String plainText, String passphrase) {
try {
final salt = genRandomWithNonZero(8);
var keyndIV = deriveKeyAndIV(passphrase, salt);
final key = encrypt.Key(keyndIV.item1);
final iv = encrypt.IV(keyndIV.item2);
final encrypter = encrypt.Encrypter(
encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7"));
final encrypted = encrypter.encrypt(plainText, iv: iv);
Uint8List encryptedBytesWithSalt = Uint8List.fromList(
createUint8ListFromString("Salted__") + salt + encrypted.bytes);
return base64.encode(encryptedBytesWithSalt);
} catch (error) {
throw error;
}
}
String decryptAESCryptoJS(String encrypted, String passphrase) {
try {
Uint8List encryptedBytesWithSalt = base64.decode(encrypted);
Uint8List encryptedBytes =
encryptedBytesWithSalt.sublist(16, encryptedBytesWithSalt.length);
final salt = encryptedBytesWithSalt.sublist(8, 16);
var keyndIV = deriveKeyAndIV(passphrase, salt);
final key = encrypt.Key(keyndIV.item1);
final iv = encrypt.IV(keyndIV.item2);
final encrypter = encrypt.Encrypter(
encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7"));
final decrypted =
encrypter.decrypt64(base64.encode(encryptedBytes), iv: iv);
return decrypted;
} catch (error) {
throw error;
}
}
Tuple2<Uint8List, Uint8List> deriveKeyAndIV(String passphrase, Uint8List salt) {
var password = createUint8ListFromString(passphrase);
Uint8List concatenatedHashes = Uint8List(0);
Uint8List currentHash = Uint8List(0);
bool enoughBytesForKey = false;
Uint8List preHash = Uint8List(0);
while (!enoughBytesForKey) {
int preHashLength = currentHash.length + password.length + salt.length;
if (currentHash.length > 0)
preHash = Uint8List.fromList(
currentHash + password + salt);
else
preHash = Uint8List.fromList(
password + salt);
currentHash = md5.convert(preHash).bytes;
concatenatedHashes = Uint8List.fromList(concatenatedHashes + currentHash);
if (concatenatedHashes.length >= 48) enoughBytesForKey = true;
}
var keyBtyes = concatenatedHashes.sublist(0, 32);
var ivBtyes = concatenatedHashes.sublist(32, 48);
return new Tuple2(keyBtyes, ivBtyes);
}
Uint8List createUint8ListFromString(String s) {
var ret = new Uint8List(s.length);
for (var i = 0; i < s.length; i++) {
ret[i] = s.codeUnitAt(i);
}
return ret;
}
Uint8List genRandomWithNonZero(int seedLength) {
final random = Random.secure();
const int randomMax = 245;
final Uint8List uint8list = Uint8List(seedLength);
for (int i=0; i < seedLength; i++) {
uint8list[i] = random.nextInt(randomMax)+1;
}
return uint8list;
}
Usage
import 'package:app/utils/cryptojs_aes_encryption_helper.dart';
String plainText = 'PlainText is Me';
var encrypted = encryptAESCryptoJS(plainText, "password");
var decrypted = decryptAESCryptoJS(encrypted, "password");
When you pass CryptoJS a string as the key it treats it as a passphrase and generates the key from it using a key derivation function - in this case PBKDF2. It generates a 256 bit key and a 128 bit initialization vector (IV). It then uses those for the encryption/decryption. You also need to find out what chaining method CryptoJS uses (probably cipher block chaining (CBC)) and what padding method it uses (to make sure that the plain text is a round number of 128 bit blocks - probably PKCS#7).
CryptoJS has this "works out of the box" mode, but it isn't particularly clear what it's doing under the hood - you'd need to read the source code or scour the documentation.
When trying to inter-operate between two systems/languages it's best if you remain in charge of things, rather than letting one end make arbitrary decisions. That way you can make sure that you have the settings the same at each end.
So, you might choose to:
Use PBKDF2 to generate a 128 bit key and 128 bit IV from the string
passphrase - with, say, 9999 rounds
Use PKCS#7 padding
Use AES in CBC mode
The pointycastle package supports all the above in Dart. It looks like CryptoJS supports all of those too.
Start with a passphrase and make sure you can generate the same key and IV in JS and Dart. Then move onto creating the ciphers.
Remember, too, never to encrypt two messages with the same key/IV pair. Use a message sequence number, for example, to slightly change the IV for each message.
Sample encrypt using nodejs script:
var key = CryptoJS.PBKDF2("123456", "123456", {
keySize: 256 / 32
});
var iv = CryptoJS.PBKDF2("123456", "123456", {
keySize: 128 / 32
});
var encrypted = CryptoJS.AES.encrypt('my message', key, { iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}).toString();
decript using dart
import 'package:encrypt/encrypt.dart' as aes;
import 'package:crypto/crypto.dart';
import 'package:hex/hex.dart';
import 'package:password_hash/pbkdf2.dart';
void main(List<String> arguments) {
String encrypted = 'HbsmGAigiIWmU3MNZAf8+w==';
final generator = PBKDF2(hashAlgorithm: sha1);
final key = aes.Key.fromBase16(HEX.encode(generator.generateKey("123456", "123456", 1, 32)));
final iv = aes.IV.fromBase16(HEX.encode(generator.generateKey("123456", "123456", 1, 16)));
final encrypter = aes.Encrypter(aes.AES(key, mode: aes.AESMode.cbc, padding: 'PKCS7'));
final decrypted = encrypter.decrypt64(encrypted, iv:iv);
print(decrypted);
}

encrypt using ase256 gives different output in python and nodejs

I am trying to encrypt a string "1" using key = "secret_key" and text "11869021012". Earlier I had written this in nodejs. now I want to port this to python. but here surprisingly both are giving different outputs.
var crypto = require('crypto');
function getBytes (str) {
let bytes = [], char;
str = encodeURI(str);
while (str.length) {
char = str.slice(0, 1);
str = str.slice(1);
if ('%' !== char) {
bytes.push(char.charCodeAt(0));
} else {
char = str.slice(0, 2);
str = str.slice(2);
bytes.push(parseInt(char, 16));
}
}
return bytes;
};
function getIV (str, bytes){
iv = getBytes(str);
if(!bytes) bytes = 16;
for(let i=iv.length;i<bytes;i++) {
iv.push(0);
}
return Buffer.from(iv);
};
function getKey (pwd){
pwd = Buffer.from(getBytes(pwd), 'utf-8');
let hash = crypto.createHash('sha256');
pwd = hash.update(pwd).digest();
return pwd;
};
function createCipherIV (algorithm, input_key, iv_input, text){
let iv = getIV(iv_input);
let key = getKey(input_key);
let cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(text)
encrypted += cipher.final('base64');
return encrypted;
}
output = createCipherIV('aes256', 'secret_key', '11869021012', '1')
console.log(output)
This produces the output:
s6LMaE/YRT6y8vr2SehLKw==
python code:
# AES 256 encryption/decryption using pycrypto library
import base64
import hashlib
from Crypto.Cipher import AES
from Crypto import Random
BLOCK_SIZE = 16
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]
password = "secret_key"
def encrypt(raw, password):
private_key = hashlib.sha256(bytearray(password, "utf-8")).digest()
raw = pad(raw)
iv = b'11869021012\x00\x00\x00\x00\x00'
cleartext = bytearray(raw, 'utf-8')
cipher = AES.new(private_key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(cleartext))
# First let us encrypt secret message
encrypted = encrypt("1", password)
print(encrypted)
This produces the output:
MTE4NjkwMjEwMTIAAAAAALOizGhP2EU+svL69knoSys=
I have used aes256 algorithm here for encrypting message.
Clearly they are very close, but node seems to be padding the output with some extra bytes. Any ideas how I can get the two to interoperate?
First, in a secure crypto system, you should expect the output to be different every time you encrypt, even using the same code. That fact that yours doesn't indicates it's an insecure cipher. Typically this is done by adding a random IV.
Your IV is "11869021012", which is horrible (because it's not random, and not even 16 bytes), but it does seem you're using it the same way in both, so that's fine.
Your password is the SHA-256 of a string, which is a horrible way to create a key, but still, you seem to be doing it the same way in both cases, so that's fine.
Your problem is that the Python code emits the IV followed by the cipher text. Your JS code does not emit the IV; it only emits the cipher text. So you probably meant this in the Python:
return base64.b64encode(cipher.encrypt(cleartext))
Or you need to rework the JavaScript to glue together the IV and the cipher text before Base64 encoding.

How to decrypt data that is encrypted by OpenSSL with RSA_PKCS1_OAEP_PADDING using NCryptDecrypt?

I have difficulty to decrypt data being encrypted using OpenSSL and RSA_PKCS1_OAEP_PADDING padding option.
What I am doing is:
BCRYPT_ALG_HANDLE hCryptAlg = NULL;
BCRYPT_OAEP_PADDING_INFO paddingInfo = { 0 };
DWORD cbDecryptedMessage;
BYTE* pbDecryptedMessage = NULL;
paddingInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM;
// Open an algorithm handle.
BCryptOpenAlgorithmProvider(&hCryptAlg, BCRYPT_RSA_ALGORITHM, NULL, 0);
// Calculate the required buffer
NCryptDecrypt(m_hKeyContextFull, (LPBYTE)pEncrypted, encryptedLenInBytes, &paddingInfo, NULL, cbDecryptedMessage, &outputDataLen, NCRYPT_PAD_OAEP_FLAG | NCRYPT_SILENT_FLAG);
// After required buffer is allocated...
NCryptDecrypt(m_hKeyContextFull, (LPBYTE)pEncrypted, encryptedLenInBytes, &paddingInfo, pbDecryptedMessage, cbDecryptedMessage, &outputDataLen, NCRYPT_PAD_OAEP_FLAG | NCRYPT_SILENT_FLAG);
It fails with NTE_INVALID_PARAMETER (0x80090027). I tried different flags but none of them works.
Note: m_hKeyContextFull has already been retrieved using CryptAcquireCertificatePrivateKey function call:
m_hSystemStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE, m_storeName.c_str());
m_pCertWithKeys = CertFindCertificateInStore(m_hSystemStore, SupportedEncodings, 0, CERT_FIND_SUBJECT_STR, m_certName.c_str(), NULL);
// Obtain the private key from the certificate.
DWORD m_KeyContextSpec = 0;
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE m_hKeyContextFull;
CryptAcquireCertificatePrivateKey(m_pCertWithKeys, CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG, NULL, &m_hKeyContextFull, &m_KeyContextSpec, &m_KeyContextMustBeReleased);
Note: All error checkings have been removed from code for readability.
Is there any idea what am I doing wrong?
Thanks.
I'd start by making paddingInfo valid:
BCRYPT_OAEP_PADDING_INFO paddingInfo;
paddingInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM;
paddingInfo.pbLabel = NULL;
paddingInfo.cbLabel = 0;
The current standard has the empty string label (see RFC 3447).
Moreover, as to pbDecryptedMessage:
If this parameter is NULL, this function will calculate the size needed for the decrypted data and return the size in the location pointed to by the pcbResult parameter.
So it won't decrypt anyway. You need to allocate the buffer first to the right size; a lot of Windows API's work that way: first call it with output buffer NULL and you get back the needed size somehow, in this case in outputDataLen (which you should declare!). Then allocate the outbufbuffer to that size, and call the function again with the fresh buffer, and the correct length in cbDecryptedMessage. After use, free the buffer again of course. But your comment claims to have done so?
Another suspicious fact: You use NCryptDecrypt so the first argument, should
be a hKey of the right type, while m_hKeyContextFull does not seem to have that type. CryptAcquireCertificatePrivateKey will get you an old style key handle.
You cannot mix these different Windows CryptoAPI's.
Maybe look at the NcryptImportKey function, to transfer it.
I'm not familiar with the NCrypt series of interfaces, but we implemented something similar recently in a library using the BCrypt series of interfaces. Here is the function in question, which can be seen in greater context here.
In our case prvblbtyp is LEGACY_RSAPRIVATE_BLOB and prvblbbuf and prvblbblen are described here.
static
int
asymmetric_decrypt(
const wchar_t * const prvblbtyp,
const void * const prvblbbuf, const size_t prvblblen,
const void * const ctbuf, const size_t ctlen,
void ** const ptbuf, size_t * const ptlen)
{
BCRYPT_ALG_HANDLE halg;
int res;
res = INT_MIN;
if (BCryptOpenAlgorithmProvider(
&halg, BCRYPT_RSA_ALGORITHM, NULL, 0) == STATUS_SUCCESS) {
BCRYPT_KEY_HANDLE hkey;
if (BCryptImportKeyPair(
halg,
NULL, prvblbtyp, &hkey,
(void *)prvblbbuf, prvblblen,
0) == STATUS_SUCCESS) {
BCRYPT_OAEP_PADDING_INFO inf;
ULONG len;
inf.pszAlgId = BCRYPT_SHA1_ALGORITHM;
inf.pbLabel = NULL;
inf.cbLabel = 0;
/*
* decrypt first with a NULL output buffer.
* this returns the size necessary for the buffer.
*/
if (BCryptDecrypt(
hkey,
(void *)ctbuf, ctlen,
&inf,
NULL, 0,
NULL, 0, &len,
BCRYPT_PAD_OAEP) == STATUS_SUCCESS) {
void * buf;
/*
* allocate the required buffer
* and decrypt again
*/
res = -ENOMEM;
buf = malloc(len);
if (buf) {
res = INT_MIN;
if (BCryptDecrypt(
hkey,
(void *)ctbuf, ctlen,
&inf,
NULL, 0,
buf, len, &len,
BCRYPT_PAD_OAEP) == STATUS_SUCCESS) {
*ptbuf = buf;
*ptlen = len;
res = 0;
} else {
free(buf);
}
}
}
BCryptDestroyKey(hkey);
}
BCryptCloseAlgorithmProvider(halg, 0);
}
return res;
}

Encryption Failing with error Invalid length for a Base-64 char array or string

I am trying to encrypt a PRIVATE KEY with a passphrase so I can save the file to disk. But the encryption method is throwing the exception: "Invalid length for a Base-64 char array or string".
The Encryption method is (it's a 2048bit key length):
public static string Encrypt(string plainString, string key, int keySize = Keysize.SymmetricKeyLength)
{
var aesEncryption = new RijndaelManaged
{
KeySize = keySize,
BlockSize = 128,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7,
IV = Convert.FromBase64String(Encoding.UTF8.GetString(Convert.FromBase64String(key)).Split(',')[0]),
Key = Convert.FromBase64String(Encoding.UTF8.GetString(Convert.FromBase64String(key)).Split(',')[1])
};
byte[] plainText = Encoding.UTF8.GetBytes(plainString);
ICryptoTransform crypto = aesEncryption.CreateEncryptor();
// The result of the encryption and decryption
byte[] cipherText = crypto.TransformFinalBlock(plainText, 0, plainText.Length);
return Convert.ToBase64String(cipherText);
}
I am converting the Private Key to a Base64 string before passing it down to the encryption method using this method:
public string EncodeTo64(string plainString)
{
var bytes = Encoding.UTF8.GetBytes(plainString);
return Convert.ToBase64String(bytes);
}
The Private Key is:
<RSAKeyValue><Modulus>rhtMjLTg17sYlns4ktTow9eeuwRNra0+AO2HqESGmA8zkxe/uOv0msXzzLWUWzdPaTxi4OV+PNVPBAHW1C0CTT/33NlvipkJ1Qr5BJK1TiVZCMInshe4OL/7GNnPUPhsS6DZ/c/fnWLoxtRMUmkKgpWmtXGs7ZSoIztdJ1bgiygJWCDvrHTokVIzDaNzRonZIFk41Qt4rPofCEawjkR639OcOfazNlmU9JjvRs3ysoYghDzvVuLvJvPK7zCMzpJMQFQE7cipezXbumTqSdp20mQXJduDbD9qLKXOvcTw+2KPoNlUp+IRQrOmSf+Dl4Vxi+8+UuOH7KDLz7yL9IOPeQ==</Modulus><Exponent>AQAB</Exponent><P>2AHey3Tgg/K8M16kv6bWk1BsFGhg9xXZw2ruVhS620gyvPBtWBuOU+tzPRnEplw+Kp9jua7Nu4JkKwpQdZvRqeW42d/UCergkdNRheM3DXYj/xQNs8a1diTNe72elCsCfSHr1z/vgN+Cp+v8O4BzX07TrHeGOOP/7HWhE6setxM=</P><Q>zld05TyC/vVI2sBgaR/iYyXdUO3iIIwkGSyOmfDr1dbCKFR7btGLEsW9EpCGibyGPbAk4jA9BLU1bviBM8iH6mxWn1s4UAiIha0QSM2K9NWUPi67FELl6Fs2eLHl9qRniBhAOBCGArklail+YadKCtUsrWhfJgvO3uxkp+fg9MM=</Q><DP>sziaCmVnAxObY2PbfciHsKLBig0wptHSZHmMVo/MmbRFpM43aysx5B8u9jszFnTif6rPq3iF6lY9lhhwuaQXScf4n40++RuQSG307gmf2+Nx6mpRFCCC3wuaElk6AeXNotVKQMYjieHpHjqGhTgGgcV9i1OAYiOKbD8M7qzER1E=</DP><DQ>FEazzfLsTHF9/0D4OFxRurx1ywYVOm2K/o5KVQY/pnu8CIqEtpcQu3+C3Ngm4FIOPvGYLkHfPR8xaP4ydAw4juimenJUTkkIYVpoRz8rcHOsZY/iAlOwk+yipamVl28AXXdEmD3HbW0UKCJ7sMznkbjw8vlWoD54zZ8dJQK8MFE=</DQ><InverseQ>FUFC9v5B1mXxbbiD4WZm/KGIa3XO5+K9FwSRroj8wNMt+JY5aMS8SfUcrZMvUXfHS9+3BYXBIlxPBUm6HnfB7yPE9S0LFzRpB7APbJ0HVIlSjMS9ZdkqxShGAEufYx/FKQXomJlEXXkpgAiDnUnCR2H+ekQf1YpQUzol2KedwfA=</InverseQ><D>gEhc/s/HWyzf0QC5jnaRirs0mVdyZKVhKg3aBoF3KlMJDThSa05vzBpOqGaiCROXz1JPCKYPfYMt1SYFxA/lwkV/u5n6NYTNWcvb7yKptAqQr4Ne/Dm94xKRUJ4rwt1H7fF2rSyc9roKCXYjRhVfSRg63TYE1IjT2iHDYVkB2YVPK67O2O7YmQXeUHMRMVwXpnZCvweleRKlYbVFx2N7ZEC1TZoUn2RKsiBEem1eNSwnLa4wUf1Xl8Q8h+ziY0GnREf9JpTZhJW7f4MKsqLyOMgmoskKiIOWlnwq/b01ivB2CXFhxiiVuNUPPiMuJu6bhljeulvKl32kEzLAFxm2gQ==</D></RSAKeyValue>
and the resulting Base64 string from the above conversion method is:
PFJTQUtleVZhbHVlPjxNb2R1bHVzPnJodE1qTFRnMTdzWWxuczRrdFRvdzllZXV3Uk5yYTArQU8ySHFFU0dtQTh6a3hlL3VPdjBtc1h6ekxXVVd6ZFBhVHhpNE9WK1BOVlBCQUhXMUMwQ1RULzMzTmx2aXBrSjFRcjVCSksxVGlWWkNNSW5zaGU0T0wvN0dOblBVUGhzUzZEWi9jL2ZuV0xveHRSTVVta0tncFdtdFhHczdaU29JenRkSjFiZ2l5Z0pXQ0R2ckhUb2tWSXpEYU56Um9uWklGazQxUXQ0clBvZkNFYXdqa1I2MzlPY09mYXpObG1VOUpqdlJzM3lzb1lnaER6dlZ1THZKdlBLN3pDTXpwSk1RRlFFN2NpcGV6WGJ1bVRxU2RwMjBtUVhKZHVEYkQ5cUxLWE92Y1R3KzJLUG9ObFVwK0lSUXJPbVNmK0RsNFZ4aSs4K1V1T0g3S0RMejd5TDlJT1BlUT09PC9Nb2R1bHVzPjxFeHBvbmVudD5BUUFCPC9FeHBvbmVudD48UD4yQUhleTNUZ2cvSzhNMTZrdjZiV2sxQnNGR2hnOXhYWncycnVWaFM2MjBneXZQQnRXQnVPVSt0elBSbkVwbHcrS3A5anVhN051NEprS3dwUWRadlJxZVc0MmQvVUNlcmdrZE5SaGVNM0RYWWoveFFOczhhMWRpVE5lNzJlbENzQ2ZTSHIxei92Z04rQ3ArdjhPNEJ6WDA3VHJIZUdPT1AvN0hXaEU2c2V0eE09PC9QPjxRPnpsZDA1VHlDL3ZWSTJzQmdhUi9pWXlYZFVPM2lJSXdrR1N5T21mRHIxZGJDS0ZSN2J0R0xFc1c5RXBDR2lieUdQYkFrNGpBOUJMVTFidmlCTThpSDZteFduMXM0VUFpSWhhMFFTTTJLOU5XVVBpNjdGRUxsNkZzMmVMSGw5cVJuaUJoQU9CQ0dBcmtsYWlsK1lhZEtDdFVzcldoZkpndk8zdXhrcCtmZzlNTT08L1E+PERQPnN6aWFDbVZuQXhPYlkyUGJmY2lIc0tMQmlnMHdwdEhTWkhtTVZvL01tYlJGcE00M2F5c3g1Qjh1OWpzekZuVGlmNnJQcTNpRjZsWTlsaGh3dWFRWFNjZjRuNDArK1J1UVNHMzA3Z21mMitOeDZtcFJGQ0NDM3d1YUVsazZBZVhOb3RWS1FNWWppZUhwSGpxR2hUZ0dnY1Y5aTFPQVlpT0tiRDhNN3F6RVIxRT08L0RQPjxEUT5GRWF6emZMc1RIRjkvMEQ0T0Z4UnVyeDF5d1lWT20ySy9vNUtWUVkvcG51OENJcUV0cGNRdTMrQzNOZ200RklPUHZHWUxrSGZQUjh4YVA0eWRBdzRqdWltZW5KVVRra0lZVnBvUno4cmNIT3NaWS9pQWxPd2sreWlwYW1WbDI4QVhYZEVtRDNIYlcwVUtDSjdzTXpua2Jqdzh2bFdvRDU0elo4ZEpRSzhNRkU9PC9EUT48SW52ZXJzZVE+RlVGQzl2NUIxbVh4YmJpRDRXWm0vS0dJYTNYTzUrSzlGd1NScm9qOHdOTXQrSlk1YU1TOFNmVWNyWk12VVhmSFM5KzNCWVhCSWx4UEJVbTZIbmZCN3lQRTlTMExGelJwQjdBUGJKMEhWSWxTak1TOVpka3F4U2hHQUV1Zll4L0ZLUVhvbUpsRVhYa3BnQWlEblVuQ1IySCtla1FmMVlwUVV6b2wyS2Vkd2ZBPTwvSW52ZXJzZVE+PEQ+Z0VoYy9zL0hXeXpmMFFDNWpuYVJpcnMwbVZkeVpLVmhLZzNhQm9GM0tsTUpEVGhTYTA1dnpCcE9xR2FpQ1JPWHoxSlBDS1lQZllNdDFTWUZ4QS9sd2tWL3U1bjZOWVROV2N2Yjd5S3B0QXFRcjROZS9EbTk0eEtSVUo0cnd0MUg3ZkYyclN5Yzlyb0tDWFlqUmhWZlNSZzYzVFlFMUlqVDJpSERZVmtCMllWUEs2N08yTzdZbVFYZVVITVJNVndYcG5aQ3Z3ZWxlUktsWWJWRngyTjdaRUMxVFpvVW4yUktzaUJFZW0xZU5Td25MYTR3VWYxWGw4UThoK3ppWTBHblJFZjlKcFRaaEpXN2Y0TUtzcUx5T01nbW9za0tpSU9XbG53cS9iMDFpdkIyQ1hGaHhpaVZ1TlVQUGlNdUp1NmJobGpldWx2S2wzMmtFekxBRnhtMmdRPT08L0Q+PC9SU0FLZXlWYWx1ZT4=
Apparently this Base64 string is invalid and the encryption fails.
Can anybody see where I am going wrong?
Your Base64 string appears to be valid. When I decode it I get some recognisable XML for RSA:
<RSAKeyValue>
<Modulus>rht ... PeQ==</Modulus>
<Exponent>AQAB</Exponent>
<P>2AH ... txM=</P>
<Q>zld ... 9MM=</Q>
<DP>szi ... R1E=</DP>
<DQ>FEa ... 8MFE=</DQ>
<InverseQ>FUF ... wfA=</InverseQ>
<D>gEh ... m2gQ==</D>
</RSAKeyValue>
That has added newlines and is abbreviated for clarity.
Was that what you were expecting? If it was then I suggest that you check each of the included pieces of Base64 for errors. Alternatively, it may just be that your initial piece of Base64 is too long.
Fixed! It appears the error was a little misleading (or likely I was being stupid!) and it looks like it was NOT the plain text for encryption that was the cause of the exception but the password/passphrase.
I found another app I was using this method in and found the KEY was the problem. I was passing a plain text password in initially, then I changed this to a SHA-256 hash of the password when this was in fact not a valid key.
I added these two methods (the later of which I found on here):
public static string AesKeyFromPassword(string password, int keySize = Keysize.SymmetricKeyLength)
{
byte[] passwordByteArray = CreateKey(password);
var aesEncryption = new RijndaelManaged
{
KeySize = keySize,
BlockSize = 128,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7,
Key = passwordByteArray
};
aesEncryption.GenerateIV();
string ivStr = Convert.ToBase64String(aesEncryption.IV);
string keyStr = Convert.ToBase64String(aesEncryption.Key);
string completeKey = ivStr + "," + keyStr;
return Convert.ToBase64String(Encoding.UTF8.GetBytes(completeKey));
}
private static byte[] CreateKey(string password)
{
var salt = new byte[] { 1, 2, 23, 234, 37, 48, 134, 63, 248, 4 };
const int Iterations = 9872;
using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt, Iterations))
return rfc2898DeriveBytes.GetBytes(32);
}
This now takes the plain text password/phrase and generates a valid encryption key which I then use in the Encrypt method.
Thank for your help guys! I can now securely store the Private Key! :)

Resources