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.
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'm using C#.
I have a private key with size of 256 bytes,
I'm trying to use DPAPI as follow:
RijndaelManaged key = new RijndaelManaged();
byte[] buffer = new byte[32]
{
3,3,3,3,3,3,3,3,
5,5,5,5,5,5,5,57,
6,7,8,8,8,8,8,3,
1,33,36,39,39,39,31,37
};
byte[] secret = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
// Encrypt a copy of the data to the stream.
byte[] output = ProtectedData.Protect(buffer, secret, DataProtectionScope.CurrentUser);
key.Key = output;//Throw an exception
My problem that output array thats return from ProtectData.Protect is with size that key.Key isn't supported (178 bytes) and when i'm trying to insert the output into that RijndaelManaged key i'm got an exception:
'System.Security.Cryptography.CryptographicException' occurred in mscorlib.dll
Additional information: Specified key is not a valid size for this algorithm.
How can i solve it? or any another solution to store my RijndaelManaged key?
I want also to access to my private key from another proccess
Thanks.
The output of ProtectedData.Protect is encrypted (not an encryption key). It grows to store whatever context and integrity checking it needs to prove that it can decrypt correctly. To get your original 256-bit key back you would need to call Unprotect.
If you're trying to derive a key (instead of encrypt it) use a key derivation routine, like PBKDF2 (in .NET this is implemented by Rfc2898DeriveBytes).
Alternatively, if you're trying to use DPAPI to protect data, it does that inherently; you don't get to customize a key for it... just pass it the data to protect.
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
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).
A bit more background info as suggested:
I'm finsihing of an Intranet CMS web app where I have to use the products API (ASP.NET based). Because of time constraints and issues with Windows authen' I need another way to ensure staff do not need to re login everytime they visit the site to view personalised content. The way it works is that once a user logs in (username/password), a Session ID storing a new different Security context value is generated that is used to display the personalised content. The API login method called uses the username and password as parameters. The only way I can think of automatically logging in the next time the staff visits the site is by storing the password in a enrypted cookie and checking of its existing when the site is visited and then calling the API login method using the username and decrypted password cookie values.
Any other ideas as an alternative welcomed.
Mo
Hi,
I'm using some code found on the web to encrypt and decrypt a password string. It encrypts fine but when it calls the code below to decrypt the string it throws the error "Length of the data to decrypt is invalid" How can I resolve this?
Thanks in advance.
Mo
System.Text.Encoding enc = System.Text.Encoding.ASCII;
byte[] myByteArray = enc.GetBytes(_pword);
SymmetricAlgorithm sa = DES.Create();
MemoryStream msDecrypt = new MemoryStream(myByteArray);
CryptoStream csDecrypt = new CryptoStream(msDecrypt, sa.CreateDecryptor(), CryptoStreamMode.Read);
byte[] decryptedTextBytes = new Byte[myByteArray.Length];
csDecrypt.Read(decryptedTextBytes, 0, myByteArray.Length);
csDecrypt.Close();
msDecrypt.Close();
string decryptedTextString = (new UnicodeEncoding()).GetString(decryptedTextBytes);
A couple of things here...
You shouldn't encrypt passwords usually. You should hash them.
If you decide to continue down the road of encryption..
You are using the DES algorithm. This is considered insecure and flawed. I'd recommend looking at the AES algorithm.
Depending on how much data you are working with, the CryptoStream might be overkill.
Using the ASCII encoding can cause loss of data that isn't ASCII, like Cyrillic letters. The recommended fix is to use something else, like UTF8.
Here is an example:
string text = "Hello";
using (var aes = new AesManaged())
{
var bytes = System.Text.Encoding.UTF8.GetBytes(text);
byte[] encryptedBytes;
using (var encrypt = aes.CreateEncryptor())
{
encryptedBytes = encrypt.TransformFinalBlock(bytes, 0, bytes.Length);
}
byte[] decryptedBytes;
using (var decrypt = aes.CreateDecryptor())
{
decryptedBytes = decrypt.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
}
var decryptedText = System.Text.Encoding.UTF8.GetString(decryptedBytes);
Console.Out.WriteLine("decryptedText = {0}", decryptedText);
}
This will use a random key every time. It is likely that you will need to encrypt some data, then decrypt it at a later time. When you create the AesManaged object, you can store the Key and IV property. You can re-use the same Key if you'd like, but different data should always be encrypted with a different IV (Initialization Vector). Where you store that key, is up to you. That's why hashing might be a better alternative: there is no key, and no need to worry about storing the key safely.
If you want to go down the hashing route, here is a small example:
var textToHash = "hello";
using (SHA1 sha = new SHA1Managed())
{
var bytesToHash = System.Text.Encoding.UTF8.GetBytes(textToHash);
var hash = sha.ComputeHash(bytesToHash);
string base64hash = Convert.ToBase64String(hash);
}
This uses the SHA1 algorithm, which should work fine for passwords, however you may want to consider SHA256.
The concept is simple: a hash will produce a (mostly) unique output for an input, however the output cannot be converted back to the input - it's destructive. Whenever you want to check if a user should be authenticated, check hash the password they gave you, and check it against the hash of the correct password. That way you aren't storing anything sensitive.
I've actually had this error before and it took me 3 days to figure out the solution. The issue will be the fact that the machine key you need for descryption needs to be registered on your machine itself.
Read fully up on DES encryption, it works by an application key, and a machine-level key. The error you're getting is likely because of the machine key missing.
Compare the bytes used to create the _pword string (in the encryption method) to the bytes retrieved with GetBytes. Probably you will notice a change in the data there.
To store the encrypted bytes, I think you should use Convert.ToBase64String and Convert.FromBase64String turn the encrypted password to/from a string.
I also do not see the code where you set the Key and IV. So I guess you are using a different key to encrypt and decrypt the password.
If the current Key property is null,
the GenerateKey method is called to
create a new random Key. If the
current IV property is null, the
GenerateIV method is called to create
a new random IV.
DES is a block based cipher - only certain lengths of buffers are valid. If I remember correctly, the block size for DES is 64 bits, so you need to ensure that your byte array is a multiple of 8 bytes long.
(That should fix your immediate problem, but I'd reference other peoples advice here - you really ought not to be using DES for any new code, and for passwords it's usually more appropriate to hash than to encrypt).