I am developing an integration in C# which syncs Office365 distribution lists to Sendy (sendy.co), written in PHP.
In the PHP application some ID's are being encrypted and I want to achieve the same in C# so that I can communicate using their API without having to look up the 'secret' ID.
This is the code in PHP (I replaced the password) in their application that calculates these ID's:
$encrypted = openssl_encrypt($in, 'AES-256-CBC', 'API_KEY', 0, 'SECRET_PASSWORD');
$encrypted = str_replace('/', '892', $encrypted);
$encrypted = str_replace('+', '763', $encrypted);
$encrypted = str_replace('=', '', $encrypted);
I overcame this 'issue' by hosting the PHP script somewhere and calling it from my C# application, but I want to make it opensource so I would like this to be integrated in the application.
I suppose I would have to start with .NET's AesCryptoServiceProvider, but I don't seem to be able to get it right (I get exceptions about the key length and stuff).
So far I tried this:
public static string Execute()
{
// openssl_encrypt ( string $data , string $method , string $password [, int $options = 0 [, string $iv = "" ]] )
var aes = new AesCryptoServiceProvider();
aes.KeySize = 256;
// Fixed password in code
aes.Key = Encoding.UTF8.GetBytes("FIXED PASSWORD");
// API = IV
aes.IV = Encoding.UTF8.GetBytes("SENDY API KEY");
aes.Mode = CipherMode.CBC;
// Trying to encrypt "36" in this case
byte[] src = Encoding.Unicode.GetBytes("36");
// Actual encryption
using (var encrypt = aes.CreateEncryptor())
{
byte[] dest = encrypt.TransformFinalBlock(src, 0, src.Length);
// Convert byte array to Base64 strings
return Convert.ToBase64String(dest);
}
}
However this throws an exception saying the IV doesn't match the block size of the algorithm.
I suppose the openssl_encrypt method in PHP derivatives the actual IV from the given API KEY in the sample (so the $password parameter), but I can't find much documentation on it to be able to achieve the same in C#.
The size of the iv must be equal to the block size which for AES is 16-bytes, "SENDY API KEY" is only 13-bytes.
The iv should be a different random value for each encryption, just prepend it to the encrypted data for use during decryption.
Next you have specified a key size of 256-bits but the key "FIXED PASSWORD" is only 14-bytes, make them the same, 128, 192 or 256 bits (16, 24 or 32 bytes). There is no need to use a key over 128-bits.
A password should not be used directly for a key, the key should be derived from the password with a function such as PBKFD2.
AES is a block cipher and it's. Input needs to be a multiple of the block size in length. To accomplish this padding of the input is needed. Typically PKCS#7 (née PKCS#5) padding is used, it is probably the default for OpenSSL. The encryption/decryptions should automatically add/remove this padding. But this does mean that the encrypted data length will be from 1-byte to 16-bytes longer than the input. e.g. If you encrypt "36" the output length will be 16-bytes.
Getting all this together correctly can be difficult to get correct, consider using an overall solution such as RNCryptor-php or defuse
Related
I have a client/server setup that consists of a server written in C++ using OpenSSL and a client written in C# using Aes/RSACryptoServiceProvider. I generate an RSA key pair on both sides and send each side the public key. Then, when I'm ready to send a message I generate an Aes key/iv and encrypt the message with this, and then encrypt the Aes key (and the iv too? I've tried both encrypting it and not encrypting it, but both give me the same error, which I will mention in a bit) with the public key of the recipient and then send the Aes encrypted key, the iv and the encrypted message. However, when I try to send from the client to the server, I get an OpenSSL error that reads "data greater than mod len" when using EVP_OpenInit.
Here is how I generate the data in C#:
var keyPair = new RSACryptoServiceProvider(2048); //server uses 2048 too (added on Edit)
var aes = new AesCryptoServiceProvider();
//OpenSSL uses the EVP_CIPHER* EVP_aes_256_cbc()
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.KeySize = 256; //bits
aes.GenerateKey();
aes.GenerateIV();
var message = Encoding.Default.GetBytes("test data");
var eMessage = new byte[4096];
using (var stream = new MemoryStream(eMessage))
{
var encryptor = aes.CreateEncryptor();
using (var cryptoStream = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
{
await cryptoStream.WriteAsync(message, 0, message.Length);
}
}
string eMessageString = null;
for (int i = 0; i < eMessage.Length; i++)
{
if (eMessage[i] == '\0')
{
eMessageString = Convert.ToBase64String(eMessage, 0, i-1);
}
}
var eKey = Convert.ToBase64String(keyPair.Encrypt(aes.Key, false));
var eIV = Convert.ToBase64String(aes.IV); //may not need to encrypt
I know my C++ implementation works as OpenSSL correctly reads in the client public key and I can encrypt/decrypt data using the EVP_Seal/EVP_Open functions when using a different key generated through OpenSSL on the server. So, I'm not sure what's causing this error, but I think I have an idea. Could it be the way that the key/iv/encrypted message is encoded when I'm sending the data to the server? Or could it be the differences in implementation between OpenSSL and C#? Or maybe something I'm not catching altogether?
EDIT: Here is the requested code for how I use EVP_OpenInit.
BlueSOD::Encryption::DecryptionData BlueSOD::Encryption::EncryptionFactory::Decrypt2(DecryptionWork2 && work)
{
DecryptionData data;
EVP_PKEY* privateKey = work.privateKey.get();
auto encryptionKey = (unsigned char*)work.info.key.c_str();
auto encryptionIV = (unsigned char*)work.info.iv.c_str();
EVP_CIPHER_CTX_ptr cipherCtxPtr{ AcquireCipherCtx() };
EVP_CIPHER_CTX* cipher = cipherCtxPtr.get();
int status;
//ERROR HAPPENS HERE
status = EVP_OpenInit(cipher, m_Cipher, encryptionKey, work.info.key.size(), encryptionIV, privateKey);
cout << ERR_error_string(ERR_get_error(), nullptr) << endl;
CheckForError(status, "EVP_OpenInit failed.");
int bufferLength = work.cipherText.size() + EVP_MAX_BLOCK_LENGTH;
auto buffer = make_unique<unsigned char[]>(bufferLength);
auto cipherTemp = (unsigned char*)work.cipherText.c_str();
status = EVP_OpenUpdate(cipher, buffer.get(), &bufferLength, cipherTemp, work.cipherText.size());
CheckForError(status, "EVP_OpenUpdate failed.");
status = EVP_OpenFinal(cipher, buffer.get(), &bufferLength);
CheckForError(status, "EVP_OpenFinal failed.");
data.plainText = CreateSecureString(buffer.get(), bufferLength);
return move(data);
}
Encoding.Default.GetString won't work. The IV, wrapped key and ciphertext are all binary, and (as good as) indistinguishable from random. That means that the encoding may go wrong as not all bytes will map to characters. This means that information is lost. Try and use base 64 encoding instead.
Your IV, wrapped key and ciphertext should also be distinguishable from each other. This is however not hard as the IV has the block size of the underlying cipher (16 bytes for AES/CBC), the wrapped key has the same size in bytes of the modulus (or the RSA key size), and the ciphertext, well, is the rest. In other words you might as well simply concatenate them all.
So your hunch was right.
RSA 4096 w/ OAEP can only encrypt 446 bytes of data (see 7.1 of RSA RFC 2437), and RSA 2048 w/ OAEP can only encrypt 245 bytes (still should be plenty of room for 16 + 32 bytes for IV and symmetric key). I don't see anywhere that you set the key length for the RSA provider, so it may be failing for some reason to encrypt the AES key.
Can you provide at least the line at which the server code throws the exception? What are you providing for the eki parameter (symmetric secret key length) in EVP_OpenInit? Are you performing the Base64 decoding of the symmetric key before attempting to decrypt it using RSA on the server?
And for the record, you do not need to encrypt the IV before transmitting, but it has no negative impact (other than computation cost) to do so.
Update:
It is always helpful when debugging crypto issues to reduce the number of steps in each statement so you can find where the error is occurring. I'd recommend breaking out the last few statements of your client code into individual steps and walking through them (i.e. RSA encryption and Base64-encoding on separate lines).
So you can now compare the following values on client and server and they are byte-for-byte equal (no extra 0x00, etc.)?
Reference | Client | Server
------------------------------------------------------------------
A | keyPair.Encrypt(aes.Key, false) | ek
Base64E(A) | eKey | ??
len(A) | len(A) | len(ek)
You mentioned in another comment that you compared the hex-encoded value of the Base64-decoded, encrypted key on both client and server and it was identical? Can you try just using the client & server to encrypt and decrypt an arbitrary plaintext message (<< 245 bytes in order to ensure that OAEP or PKCS#1 v1.5 padding does not then exceed 245 bytes) with that key pair to ensure everything is correct?
I'm not particularly familiar with the C# implementation -- is there something additional you need to do to replicate EVP_SealInit on the client?
My issue lied in how the message, key, and iv were encoded on the client and decoded on the server. The client encoded them in base64, so it had to be decoded on the server in order for OpenSSL to understand them (OpenSSL uses raw bytes rather than an encoding scheme).
EDIT: There also seems to be a conflict between the C# implementation and the OpenSSL implementation. The C# Aes implementation does not seem to match the OpenSSL exactly. I get the correct plain text when decrypting in OpenSSL, but there is a bunch of garbage data that follows and EVP_OpenFinal causes an error that says "bad decrypt". If anyone knows a way around this, please let me know! I have tried the openssl.net API, but it throws an error if I try to use BIO.
I have difficulties decrypting a blowfish encrypted string in a .net environment, that was encrypted by the mcrypt php library.
Here is the script I use to encrypt some data
<?php
function encrypt_blowfish($data, $key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$crypttext = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_CBC, $iv);
echo 'IV: ' . bin2hex($iv) . "\n";
echo 'DATA: ' . bin2hex($crypttext) ."\n" ;
}
$secretKey = 'somekey';
$data = 'Hello World this is an encryptiontest!';
encrypt_blowfish($data, $secretKey);
I decided to use the bouncingcastle library since it seemed to be the default choice for encryption and they had a PCL version (which I need). For testing purpose I just copy/pasted the echo'd values into my C# code.
var ivString = "34c33fed0386dda1";
var iv = Hex.Decode (ivString);
var dataString = "ced4ed218d7a1fd228f8c43ca6b83f097648811661d5510678a26953729ceccdf6d78a7695cbfe43";
var data = Hex.Decode (dataString);
var keyString = "somekey";
var key = System.Text.Encoding.UTF8.GetBytes (keyString);
var engine = new BlowfishEngine();
var cipher =new PaddedBufferedBlockCipher(new CbcBlockCipher(engine));
var keyParam = new KeyParameter(key);
cipher.Init (false, keyParam);
var outBytes = new byte[data.Length];
var len = cipher.ProcessBytes (data, 0, data.Length, outBytes, 0);
cipher.DoFinal(outBytes, len);
Console.WriteLine(System.Text.Encoding.UTF8.GetString(outBytes));
When I run this code DoFinal explodes with a "Corrupt padding block" exception. So I read about pcks7 padding which essentially fills the bytes of the original string. I calculated that for my input string and the blowfish cbc algorithm block size of 8, I would need two bytes of padding so I added "22" at the end of the string. This however yielded the same result.
Also, I don't see any point where I can insert the IV into the blowfish decryption. It feels like I am completely lacking/not understanding a vital point here. Any1 any ideas on what goes wrong here? Also if possible I would like to skip on the padding part in my php and simply decrypt with iv/passphrase in c#, is that even possible?
Cheers and thanks
Tom
I ended up using a simpler library which supperts cbc mode in a very simple fashion.
http://jaryl-lan.blogspot.de/2014/07/openfire-blowfish-encryptiondecryption.html
#Tom, your decryption problem was probably due to the '\0' padding that the PHP mcrypt library uses when it needs to make a block of text congruent to its fixed block length (64 bits, or 8 bytes). I hear .Net uses something different? Moreover, does mcrypt_create_iv() produce a binary data? Out of curisoity, why would you use bin2hex() on the IV.
The way to send the IV with with the cipher text is simply $cipherText = $iv . $cipherText. Then, they say, you can use substr() (possibly, mb_substr()) to recover the IV and the true cipher text from the composite. Use the recovered IV and your key to decrypt the cipher text.
The last step is to remove any padding that may have been added to the plain text by mcrypt.
I created a Blowfish diagnostic page here. I am on PHP 5.6.20, and I cannot get mcrypt to decrypt my data because I have a strange IV problem. The same technique I described to you does not seem to work. I posted the text of the diagnostic script on this question:
Blowfish IV Recovery Problem
Here is a working on-line version on my domain:
Blowfish Diganositc Webpage
I guess it is a good thing you tried something else. Ecryption with Blowfish seems to work fine, but even when using native PHP, there appears to be an issue with the decryption process. But, it could just be me! ;-)
I am currently using AesManaged class in C# to encrypt a plain text. It works fine.
However, it produces the same cipher text each time it encrypts same piece of data. Is there anyway I can tweak this behavior and produce different cipher text for same piece of data?
I have implemented encryption in SQL server using AES_256 algorithm and certificate. The process closely resembles with the post here: http://www.codeproject.com/Articles/662187/FIPS-Encryption-Algorithms-and-Implementation-of-A. In this process each time a plain text is encrypted, different cipher text is produced.
I want the same effect with C# code. How that can be achieved?
EDIT:
Here is how I implemented the approach suggested by Yolanda Ruiz:
Encrypt
public static string Encrypt(string plainText)
{
//Check for valid arguments.
if (String.IsNullOrEmpty(plainText)) throw new ArgumentNullException("plainText");
List<byte> encryptedList;
//Create Aes object
using (AesManaged aes = new AesManaged())
{
aes.Key = Key;
aes.GenerateIV();
encryptedList = aes.IV.ToList();
aes.BlockSize = BlockSize;
/*Here goes the standard code to encrypt the plain text - refer msdn for that*/
/*Append the encrypted stream to encryptedList*/
}
return encryptedList.ToArray().ToBase64();
}
Decrypt
public static string Decrypt(string cipherText)
{
//Check for valid arguments.
if (string.IsNullOrEmpty(cipherText)) throw new ArgumentNullException("cipherText");
string plainText;
byte[] cipherTextArray = cipherText.FromBase64();
//Create Aes object
using (AesManaged aes = new AesManaged())
{
aes.Key = Key;
aes.BlockSize = BlockSize;
aes.IV = cipherTextArray.Take(NoOfBytes).ToArray();//Extract the IV
cipherTextArray = cipherTextArray.Skip(NoOfBytes).ToArray();//Extract the actual plain text.
/*Here goes the standard code to Decrypt the cipher text - refer msdn for that*/
/*Assign the decrypted stream output to plainText*/
}
return plainText;
}
Unit Test
//Arrange
string plainText = "Sayan";
//Act
string cipherText1 = MyCrypto.Encrypt(plainText);
string cipherText2 = Crypto.Encrypt(plainText);
string plainText1 = Crypto.Decrypt(cipherText1);
string plainText2 = Crypto.Decrypt(cipherText2);
//Assert
//Check the cipher text is different everytime
Assert.AreNotEqual(cipherText1, cipherText2);
//Check that every plaintext output should match with the original
Assert.AreEqual(plainText, plainText1);
Assert.AreEqual(plainText, plainText2);
The way to do that is to use a different Initialization Vector for each encryption.
The default mode of operation in AesManaged is CBC. In this mode, when a block of plaintext is encrypted, it is first mixed with the result of the encryption of the previous block. As long as the previous ciphertext block is always different, this prevents two similar blocks of plaintext to output the same ciphertext. But what do we use for the very first block then? The initialization vector.
The IV is basically a randomized block that acts as if it was the result of encrypting an hypothetical plaintext block coming before the actual first block of plaintext.
The IV has to be kept around so we can feed it to the decryption method. As it is semantically a ciphertext block, it is usual to prepend it to the actual ciphertext. When decrypting, you would first extract the first block of ciphertext (as is, without decrypting) and use it as the IV to decrypt subsequent blocks.
The IV is not a secret. The attacker will not be able to derive the key or the first plaintext block from it. You must never reuse the same IV twice with the same key though, or you loose the randomization property.
The methods you will want to look at are AesManaged.GenerateIV(), AesManaged.BlockSize (which is in bits, keep it in mind if you use that property to extract the IV bytes from the ciphertext).
Encryption algorithms have to be deterministic (otherwise there's no way of reversing them)
If you want to get different cipher text, you'll have to change the key, or the data to be encrypted (or the actual algorithm).
Ive been having some Crypto troubles cant see find what I've done wrong. I'm trying to encrypt an AESkey using RSA on Android and Decrypt it server side using C#, but keep getting a "Bad Data" exception.
I used Base64encoding to move the encrypted key from client to server and noticed that after moving it from the client(Android App) using a JSON POST request there were a number of "\u000a" in the key making the encrypted data length 941 which led to a "Data to large for decryption" when removed in brought the length to 920 which allowed for 80 8 byte iterations and got me to where I am now with the Bad Data problem.
I have checked that the key Length and Algorithm are correct and both are set for 2048 bit key and using PKCS1Padding.
"Bad Data" Exception
This exception will be thrown in the following scenarios.
a) The RSA private key used for decryption does not match with the RSA public key that is used for encryption.
b) The binary data passed in to Decrypt() method is incorrect. This could happen if the application code made assumptions about the length of encrypted data or the data passed in does not match the exact bytes that is returned from Encrypt() method.
I get the public key on android by pulling it from the server with a GET which returns RSACryptoServiceProvider.ToXMLString(false); And use the same keystore for the private key so cant see it being 1.
And as far as I know the c# decrypter Isn't making any assumptions about the size of the encrypted data. Possibly my setting block size to 8 but thats after i knew the size of the encrypted AESkey.
I've been looking around for a solution and couldn't find one so would be grateful for any assistance. Apologies if I'm being stupid and missed something simple but I have my blinkers on if I am and just cant see it.
Java Encryption
private byte[] encryptRSA(byte [] data) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{
//instance of singleton PublicKey
AppPublicKey currKey = AppPublicKey.getInstance();
Log.d("ENCRYPT.MOD: ", currKey.getModBytes().toString());
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(new BigInteger(1,currKey.getModBytes()), new BigInteger(1,currKey.getExpBytes()));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] cipherData = cipher.doFinal(data);
Log.d("RSAENCRYPTION: ",Base64.encodeToString(cipherData, 1));
return cipherData;
}
C# Decrypt
public string DecryptString(string inputString, int dwKeySize)
{
// TODO: Add Proper Exception Handlers
CspParameters cp = new CspParameters();
cp.KeyContainerName = "real_Keystore";
RSACryptoServiceProvider rsaCryptoServiceProvider
= new RSACryptoServiceProvider(dwKeySize,cp);
int base64BlockSize = 8;
int iterations = inputString.Length / base64BlockSize;
ArrayList arrayList = new ArrayList();
for (int i = 0; i < iterations; i++)
{
byte[] encryptedBytes = Convert.FromBase64String(
inputString.Substring(base64BlockSize * i, base64BlockSize));
//Array.Reverse(encryptedBytes);
arrayList.AddRange(rsaCryptoServiceProvider.Decrypt(
encryptedBytes, false));
}
return Encoding.UTF32.GetString(arrayList.ToArray(
Type.GetType("System.Byte")) as byte[]);
}
It is not possible to feed a few bytes at a time to the RSA operation.
Furthermore it seems unlikely that the code performs the right amount of base 64 iterations (as you defined NO_PADDING for base 64 in your android, using 1 instead of the constant). Normally the output of RSA encryption won't be a multiple of 3 bytes, so you are at least one block off the mark.
You may want to take a closer look at the API functions you are using and take some time to study RSA examples on .NET. Normally RSA is only used to encrypt small amounts of data (such as symmetric data encryption keys) so you should be able to decode all of the base64 data in one go.
Please test your input and output in a debugger. Encryption/decryption problems normally require that the exact input and output of the encryption/decryption algorithms are compared .
I would like to encrypt and decrypt strings with a password. I use C# and WinRT (MetroStyle). Have somebody a class for encryption/decryption?
The normal .Net System.Security.Cryptography namespace does not exist in Metro. You use the CryptographicEngine class in Windows.Security.Cryptography.Core namespace instead.
If the password is only being verified/authenticated, do not encrypt it. Instead, use the following:
using Windows.Security.Cryptography.Core;
using Windows.Security.Cryptography;
using Windows.Storage.Streams;
...
// Use Password Based Key Derivation Function 2 (PBKDF2 or RFC2898)
KeyDerivationAlgorithmProvider pbkdf2 =
KeyDerivationAlgorithmProvider.OpenAlgorithm(
KeyDerivationAlgorithmNames.Pbkdf2Sha256);
// Do not store passwords in strings if you can avoid them. The
// password may be retained in memory until it is garbage collected.
// Crashing the application and looking at the memory dump may
// reveal it.
IBuffer passwordBuffer =
CryptographicBuffer.ConvertStringToBinary("password",
BinaryStringEncoding.Utf8);
CryptographicKey key = pbkdf2.CreateKey(passwordBuffer);
// Use random salt and 10,000 iterations. Store the salt along with
// the derviedBytes (see below).
IBuffer salt = CryptographicBuffer.GenerateRandom(32);
KeyDerivationParameters parameters =
KeyDerivationParameters.BuildForPbkdf2(salt, 10000);
// Store the returned 32 bytes along with the salt for later verification
byte[] derviedBytes =
CryptographicEngine.DeriveKeyMaterial(key, parameters, 32).ToArray();
When a password is supplied run through the same process using the same salt and compare derivedBytes. Store the secret as you would an encryption key.
If the password will be used, such as to connect to another service:
// Use AES, CBC mode with PKCS#7 padding (good default choice)
SymmetricKeyAlgorithmProvider aesCbcPkcs7 =
SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesCbcPkcs7);
// Create an AES 128-bit (16 byte) key
CryptographicKey key =
aesCbcPkcs7.CreateSymmetricKey(CryptographicBuffer.GenerateRandom(16));
// Creata a 16 byte initialization vector
IBuffer iv = CryptographicBuffer.GenerateRandom(aesCbcPkcs7.BlockLength);
// Encrypt the data
byte[] plainText = Encoding.UTF8.GetBytes("Hello, world!"); // Data to encrypt
byte[] cipherText = CryptographicEngine.Encrypt(
key, plainText.AsBuffer(), iv).ToArray();
// Decrypt the data
string newPlainText = new string(
Encoding.UTF8.GetChars(CryptographicEngine.Decrypt(
key, cipherText.AsBuffer(), iv).ToArray()));
// newPlainText contains "Hello, world!"
As with any cryptography, make sure to protect your keys appropriately and follow best practise. The linked documentation also provides examples.