I am trying the most basic thing - encrypt data using the public key and decrypt using private key:
X509Certificate2 cert = new X509Certificate2(#"c:\temp\CERT\mycert.pfx", "test1");
RSACryptoServiceProvider privateKey = cert.PrivateKey as RSACryptoServiceProvider;
RSACryptoServiceProvider publicKey = cert.PublicKey.Key as RSACryptoServiceProvider;
UnicodeEncoding bytConvertor = new UnicodeEncoding();
byte[] plainData = bytConvertor.GetBytes("Sample data");
byte[] enData = publicKey.Encrypt(plainData, true);
Console.WriteLine("Encrypted Output: {0}", bytConvertor.GetString(enData));
byte[] deData = privateKey.Decrypt(enData, true);
Console.WriteLine("Decrypted Output: {0}", bytConvertor.GetString(deData));
But the 2nd last line privateKey.Decrypt(...) throws the following exception:
System.Security.Cryptography.CryptographicException was unhandled
Message=Bad Key.
Source=mscorlib StackTrace:
at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
at System.Security.Cryptography.RSACryptoServiceProvider.DecryptKey(SafeKeyHandle
pKeyContext, Byte[] pbEncryptedKey, Int32 cbEncryptedKey, Boolean
fOAEP, ObjectHandleOnStack ohRetDecryptedKey)
at System.Security.Cryptography.RSACryptoServiceProvider.Decrypt(Byte[]
rgb, Boolean fOAEP)
at ConsoleApplication4.Program.Main(String[] args) in `c:\users\kazia\documents\visual studio`
`2010\Projects\ConsoleApplication4\Program.cs`:line 44 ...
InnerException:
I must be missing something obvious. What is the standard way to use RSA encryption in both end (public and private) using .NET? Any help would be appreciated.
Thanks!
Found the answer here:
http://msdn.microsoft.com/en-us/library/ms148409.aspx
Great example:
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.IO;
using System.Text;
// To run this sample use the Certificate Creation Tool (Makecert.exe) to generate a test X.509 certificate and
// place it in the local user store.
// To generate an exchange key and make the key exportable run the following command from a Visual Studio command prompt:
//makecert -r -pe -n "CN=CERT_SIGN_TEST_CERT" -b 01/01/2010 -e 01/01/2012 -sky exchange -ss my
namespace X509CertEncrypt
{
class Program
{
// Path variables for source, encryption, and
// decryption folders. Must end with a backslash.
private static string encrFolder = #"C:\Encrypt\";
private static string decrFolder = #"C:\Decrypt\";
private static string originalFile = "TestData.txt";
private static string encryptedFile = "TestData.enc";
static void Main(string[] args)
{
// Create an input file with test data.
StreamWriter sw = File.CreateText(originalFile);
sw.WriteLine("Test data to be encrypted");
sw.Close();
// Get the certifcate to use to encrypt the key.
X509Certificate2 cert = GetCertificateFromStore("CN=CERT_SIGN_TEST_CERT");
if (cert == null)
{
Console.WriteLine("Certificatge 'CN=CERT_SIGN_TEST_CERT' not found.");
Console.ReadLine();
}
// Encrypt the file using the public key from the certificate.
EncryptFile(originalFile, (RSACryptoServiceProvider)cert.PublicKey.Key);
// Decrypt the file using the private key from the certificate.
DecryptFile(encryptedFile, (RSACryptoServiceProvider)cert.PrivateKey);
//Display the original data and the decrypted data.
Console.WriteLine("Original: {0}", File.ReadAllText(originalFile));
Console.WriteLine("Round Trip: {0}", File.ReadAllText(decrFolder + originalFile));
Console.WriteLine("Press the Enter key to exit.");
Console.ReadLine();
}
private static X509Certificate2 GetCertificateFromStore(string certName)
{
// Get the certificate store for the current user.
X509Store store = new X509Store(StoreLocation.CurrentUser);
try
{
store.Open(OpenFlags.ReadOnly);
// Place all certificates in an X509Certificate2Collection object.
X509Certificate2Collection certCollection = store.Certificates;
// If using a certificate with a trusted root you do not need to FindByTimeValid, instead:
// currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certName, true);
X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
X509Certificate2Collection signingCert = currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certName, false);
if (signingCert.Count == 0)
return null;
// Return the first certificate in the collection, has the right name and is current.
return signingCert[0];
}
finally
{
store.Close();
}
}
// Encrypt a file using a public key.
private static void EncryptFile(string inFile, RSACryptoServiceProvider rsaPublicKey)
{
using (AesManaged aesManaged = new AesManaged())
{
// Create instance of AesManaged for
// symetric encryption of the data.
aesManaged.KeySize = 256;
aesManaged.BlockSize = 128;
aesManaged.Mode = CipherMode.CBC;
using (ICryptoTransform transform = aesManaged.CreateEncryptor())
{
RSAPKCS1KeyExchangeFormatter keyFormatter = new RSAPKCS1KeyExchangeFormatter(rsaPublicKey);
byte[] keyEncrypted = keyFormatter.CreateKeyExchange(aesManaged.Key, aesManaged.GetType());
// Create byte arrays to contain
// the length values of the key and IV.
byte[] LenK = new byte[4];
byte[] LenIV = new byte[4];
int lKey = keyEncrypted.Length;
LenK = BitConverter.GetBytes(lKey);
int lIV = aesManaged.IV.Length;
LenIV = BitConverter.GetBytes(lIV);
// Write the following to the FileStream
// for the encrypted file (outFs):
// - length of the key
// - length of the IV
// - ecrypted key
// - the IV
// - the encrypted cipher content
int startFileName = inFile.LastIndexOf("\\") + 1;
// Change the file's extension to ".enc"
string outFile = encrFolder + inFile.Substring(startFileName, inFile.LastIndexOf(".") - startFileName) + ".enc";
Directory.CreateDirectory(encrFolder);
using (FileStream outFs = new FileStream(outFile, FileMode.Create))
{
outFs.Write(LenK, 0, 4);
outFs.Write(LenIV, 0, 4);
outFs.Write(keyEncrypted, 0, lKey);
outFs.Write(aesManaged.IV, 0, lIV);
// Now write the cipher text using
// a CryptoStream for encrypting.
using (CryptoStream outStreamEncrypted = new CryptoStream(outFs, transform, CryptoStreamMode.Write))
{
// By encrypting a chunk at
// a time, you can save memory
// and accommodate large files.
int count = 0;
int offset = 0;
// blockSizeBytes can be any arbitrary size.
int blockSizeBytes = aesManaged.BlockSize / 8;
byte[] data = new byte[blockSizeBytes];
int bytesRead = 0;
using (FileStream inFs = new FileStream(inFile, FileMode.Open))
{
do
{
count = inFs.Read(data, 0, blockSizeBytes);
offset += count;
outStreamEncrypted.Write(data, 0, count);
bytesRead += blockSizeBytes;
}
while (count > 0);
inFs.Close();
}
outStreamEncrypted.FlushFinalBlock();
outStreamEncrypted.Close();
}
outFs.Close();
}
}
}
}
// Decrypt a file using a private key.
private static void DecryptFile(string inFile, RSACryptoServiceProvider rsaPrivateKey)
{
// Create instance of AesManaged for
// symetric decryption of the data.
using (AesManaged aesManaged = new AesManaged())
{
aesManaged.KeySize = 256;
aesManaged.BlockSize = 128;
aesManaged.Mode = CipherMode.CBC;
// Create byte arrays to get the length of
// the encrypted key and IV.
// These values were stored as 4 bytes each
// at the beginning of the encrypted package.
byte[] LenK = new byte[4];
byte[] LenIV = new byte[4];
// Consruct the file name for the decrypted file.
string outFile = decrFolder + inFile.Substring(0, inFile.LastIndexOf(".")) + ".txt";
// Use FileStream objects to read the encrypted
// file (inFs) and save the decrypted file (outFs).
using (FileStream inFs = new FileStream(encrFolder + inFile, FileMode.Open))
{
inFs.Seek(0, SeekOrigin.Begin);
inFs.Seek(0, SeekOrigin.Begin);
inFs.Read(LenK, 0, 3);
inFs.Seek(4, SeekOrigin.Begin);
inFs.Read(LenIV, 0, 3);
// Convert the lengths to integer values.
int lenK = BitConverter.ToInt32(LenK, 0);
int lenIV = BitConverter.ToInt32(LenIV, 0);
// Determine the start postition of
// the ciphter text (startC)
// and its length(lenC).
int startC = lenK + lenIV + 8;
int lenC = (int)inFs.Length - startC;
// Create the byte arrays for
// the encrypted AesManaged key,
// the IV, and the cipher text.
byte[] KeyEncrypted = new byte[lenK];
byte[] IV = new byte[lenIV];
// Extract the key and IV
// starting from index 8
// after the length values.
inFs.Seek(8, SeekOrigin.Begin);
inFs.Read(KeyEncrypted, 0, lenK);
inFs.Seek(8 + lenK, SeekOrigin.Begin);
inFs.Read(IV, 0, lenIV);
Directory.CreateDirectory(decrFolder);
// Use RSACryptoServiceProvider
// to decrypt the AesManaged key.
byte[] KeyDecrypted = rsaPrivateKey.Decrypt(KeyEncrypted, false);
// Decrypt the key.
using (ICryptoTransform transform = aesManaged.CreateDecryptor(KeyDecrypted, IV))
{
// Decrypt the cipher text from
// from the FileSteam of the encrypted
// file (inFs) into the FileStream
// for the decrypted file (outFs).
using (FileStream outFs = new FileStream(outFile, FileMode.Create))
{
int count = 0;
int offset = 0;
int blockSizeBytes = aesManaged.BlockSize / 8;
byte[] data = new byte[blockSizeBytes];
// By decrypting a chunk a time,
// you can save memory and
// accommodate large files.
// Start at the beginning
// of the cipher text.
inFs.Seek(startC, SeekOrigin.Begin);
using (CryptoStream outStreamDecrypted = new CryptoStream(outFs, transform, CryptoStreamMode.Write))
{
do
{
count = inFs.Read(data, 0, blockSizeBytes);
offset += count;
outStreamDecrypted.Write(data, 0, count);
}
while (count > 0);
outStreamDecrypted.FlushFinalBlock();
outStreamDecrypted.Close();
}
outFs.Close();
}
inFs.Close();
}
}
}
}
}
}
Nothing wrong with your code, the only thing that needs changing is the way you generated your key pair. In the answer:
//makecert -r -pe -n "CN=CERT_SIGN_TEST_CERT" -b 01/01/2010 -e 01/01/2012 -sky exchange -ss my
The relevant part is -sky exchange, which was probably omitted from the call to makecert.exe when you first reported the "Bad Key" error on Decrypt.
Related
I am currently working on transforming my C# AES-GCM cryptography code to PHP. However, after some research, the text encrypted by my PHP system cannot be decrypted by the C# one. I want to know if there is any difference from both codes:
C# with BouncyCastle:
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using System;
using System.IO;
using System.Text;
//the helper for all AES methods
public class AESHelper {
private const int KEY_BIT_SIZE = 256;
private const int MAC_BIT_SIZE = 128;
private const int NONCE_BIT_SIZE = 128;
private readonly SecureRandom random;
private static AESHelper instance;
public static AESHelper Instance //property of this class. Create an instance if it is not created yet
{
get
{
if (instance == null)
instance = new AESHelper();
return instance;
}
}
public AESHelper()
{
random = new SecureRandom();
}
//decrypt with strings
public string Decrypt(string message, string key, int nonSecretPayloadLength = 0)
{
if (string.IsNullOrEmpty(message))
throw new ArgumentException("Message required!", "message");
var decodedKey = Convert.FromBase64String(key);
var cipherText = Convert.FromBase64String(message);
var plainText = DecryptWithKey(cipherText, decodedKey, nonSecretPayloadLength);
return Encoding.UTF8.GetString(plainText);
}
//encrypt with strings
public string Encrypt(string text, string key, byte[] nonSecretPayload = null)
{
if (string.IsNullOrEmpty(text))
throw new ArgumentException("Text required!", "text");
var decodedKey = Convert.FromBase64String(key);
var plainText = Encoding.UTF8.GetBytes(text);
var cipherText = EncryptWithKey(plainText, decodedKey, nonSecretPayload);
return Convert.ToBase64String(cipherText);
}
//create new key
public string NewKey()
{
var key = new byte[KEY_BIT_SIZE / 8];
random.NextBytes(key);
return Convert.ToBase64String(key);
}
//decrypt with byte array
private byte[] DecryptWithKey(byte[] message, byte[] key, int nonSecretPayloadLength = 0)
{
if (key == null || key.Length != KEY_BIT_SIZE / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KEY_BIT_SIZE), "key");
if (message == null || message.Length == 0)
throw new ArgumentException("Message required!", "message");
using (var cipherStream = new MemoryStream(message))
using (var cipherReader = new BinaryReader(cipherStream))
{
var nonSecretPayload = cipherReader.ReadBytes(nonSecretPayloadLength);
var nonce = cipherReader.ReadBytes(NONCE_BIT_SIZE / 8);
var cipher = new GcmBlockCipher(new AesEngine());
var parameters = new AeadParameters(new KeyParameter(key), MAC_BIT_SIZE, nonce, nonSecretPayload);
cipher.Init(false, parameters);
var cipherText = cipherReader.ReadBytes(message.Length - nonSecretPayloadLength - nonce.Length);
var plainText = new byte[cipher.GetOutputSize(cipherText.Length)];
try
{
var len = cipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0);
cipher.DoFinal(plainText, len);
}
catch (InvalidCipherTextException)
{
return null;
}
return plainText;
}
}
//encrypt with byte array
private byte[] EncryptWithKey(byte[] text, byte[] key, byte[] nonSecretPayload = null)
{
if (key == null || key.Length != KEY_BIT_SIZE / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KEY_BIT_SIZE), "key");
nonSecretPayload = nonSecretPayload ?? new byte[] { };
var nonce = new byte[NONCE_BIT_SIZE / 8];
random.NextBytes(nonce, 0, nonce.Length);
var cipher = new GcmBlockCipher(new AesEngine());
var parameters = new AeadParameters(new KeyParameter(key), MAC_BIT_SIZE, nonce, nonSecretPayload);
cipher.Init(true, parameters);
var cipherText = new byte[cipher.GetOutputSize(text.Length)];
var len = cipher.ProcessBytes(text, 0, text.Length, cipherText, 0);
cipher.DoFinal(cipherText, len);
using (var combinedStream = new MemoryStream())
{
using (var binaryWriter = new BinaryWriter(combinedStream))
{
binaryWriter.Write(nonSecretPayload);
binaryWriter.Write(nonce);
binaryWriter.Write(cipherText);
}
return combinedStream.ToArray();
}
}
}
Here is the PHP system:
<?php
echo '<pre>';
$hash_string = 'qIANSOwtdfF4y5Yk33ZLE5s6KwKBAeu6qzJRG84Sjjo=';
echo "password : ";
var_dump($hash_string);
echo '<hr>';
$decode_string = base64_decode($hash_string);
$app_cc_aes_key = substr($decode_string, 0, 32);
$cipher = 'aes-256-gcm';
$iv_len = openssl_cipher_iv_length($cipher);
echo "app_cc_aes_key : ";
var_dump($app_cc_aes_key);
echo '<br>';
echo "cipher :";
var_dump($cipher);
echo '<hr>';
$data = '7bc9d6ae-982f-11e9-bc42-526af7764f64';
echo "data : {$data}";
echo '<hr>';
$tag_length = 16;
$iv = openssl_random_pseudo_bytes($iv_len);
$tag = "";
$encrypt = openssl_encrypt($data, $cipher, $app_cc_aes_key, OPENSSL_RAW_DATA, $iv, $tag, "", $tag_length);
$encrypt_text = base64_encode($iv.$tag.$encrypt);
echo "encrypt :";
var_dump($encrypt);
echo '<br>';
echo "encrypt_text :";
var_dump($encrypt_text);
echo '<hr>';
$decoded_text = base64_decode($encrypt_text);
$iv = substr($decoded_text, 0, $iv_len);
$tag = substr($decoded_text, $iv_len, $tag_length);
$ciphertext = substr($decoded_text, $iv_len + $tag_length);
$decrypt_text = openssl_decrypt($ciphertext, $cipher, $app_cc_aes_key, OPENSSL_RAW_DATA, $iv, $tag);
echo "decrypt_text : {$decrypt_text}";
echo '<hr>';
?>
Can anyone tell me if there is something missing or different in the PHP code that makes them done differently? Or if there is some internal difference between the PHP functions and the BouncyCastle functions that make them different?
In the C#-code, the data are concatenated in the following order during encryption:
nonSecretPyload nonce cipherText
Here cipherText consists of two parts, the encrypted message and the authentication tag. Appending the tag to the encrypted message is done automatically by GcmBlockCipher#DoFinal.
In the PHP-code, the data are concatenated in the following order during encryption:
$iv $tag $encrypt
Here $iv is the counterpart to nonce. In contrast to GcmBlockCipher#DoFinal, the PHP-method openssl_encrypt returns only the encrypted message ($encrypt). The authentication tag is returned in a separate variable (6th openssl_encrypt-parameter $tag). Therefore, $tag and $encrypt correspond in reverse order to cipherText. The additional authenticated data, i.e. the counterpart to nonSecretPyload are not considered in the PHP-code at all.
It is immediately apparent that the orders of the individual components in the two codes are different. This means that a message encrypted in the C#-code cannot be decrypted in the PHP-code (and vice versa). For this to be possible, the order in the PHP-code must be changed as follows:
$aad $iv $encrypt $tag
Here $aad is the counterpart to nonSecretPyload. The order (as well the consideration of the additional authenticated data) must be adapted both in the encryption part and in the decryption part.
In addition, different IV lengths are used: In the C#-code 16 bytes, in the PHP-code 12 bytes (the latter because openssl_cipher_iv_length('aes-256-gcm') returns 12), where 12 bytes is actually the recommended length. For compatibility, a uniform IV length must be used in both codes!
I am working on re-writing our encryption class to be FIPS compliant, and in doing so have to re-work how we're handling non-secret payload data. At the moment, I'm writing out the size of my non-secret payload, then writing the size of my IV. I follow that up by writing my non-secret payload and IV, with all of these writes sharing a BinaryWriter. Lastly, I then share the same MemoryStream and write my the data needing to be encrypted into the the CryptoStream.
This is what the class currently looks like:
public class Encryption
{
private const int SaltBlockSize = 8;
private const int SaltBitSize = 64;
private const int KeyBitSize = 256;
private const int SaltIterations = 10000;
private const int nonSecretPayloadOffsetInPayload = 0;
private const int ivOffsetInPayload = 1;
public byte[] GetNonSecretPayload(byte[] completePayload)
{
byte[] nonSecretPayload;
using (var memoryStream = new MemoryStream(completePayload))
{
using (var binaryReader = new BinaryReader(memoryStream))
{
int nonSecretPayloadLength = binaryReader.ReadInt32();
binaryReader.BaseStream.Position = 3;
nonSecretPayload = binaryReader.ReadBytes(nonSecretPayloadLength);
}
}
return nonSecretPayload;
}
public byte[] EncryptMessageWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null)
{
if (string.IsNullOrEmpty(password))
{
throw new InvalidOperationException("You can not provide an empty password, you must give a string that is at least 12 characters in size. If you just want to obfuscate the message without any protection, an alternative way is to use a Base64 String");
}
else if (password.Length < 12)
{
throw new InvalidOperationException("The minimum size your password can be is 12 characters.");
}
byte[] saltHash;
byte[] saltKey = this.CreateSaltKeysFromPassword(password, 0, out saltHash);
byte[] encryptedValue = null;
using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider())
{
aesProvider.Key = saltKey;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Padding = PaddingMode.PKCS7;
aesProvider.GenerateIV();
using (MemoryStream memoryStream = new MemoryStream())
{
// Write our IV out first so we can pull the IV off later during decryption.
// The IV does not need to be encrypted, it is safe to store as as unencrypted buffer in the encrypted byte array.
using (BinaryWriter ivWriter = new BinaryWriter(memoryStream, Encoding.UTF8, true))
{
// The first two writes to the stream should be the size of the non-secret payload
// and the size of the IV. If no payload exists, then we write 0.
if (nonSecretPayload == null || nonSecretPayload.Length == 0)
{
ivWriter.Write(0);
}
else
{
ivWriter.Write(nonSecretPayload.Length);
}
ivWriter.Write(aesProvider.IV.Length);
// If we have a payload, write it out.
if (nonSecretPayload != null && nonSecretPayload.Length > 0)
{
ivWriter.Write(nonSecretPayload);
}
// Write the Initialization Vector.
ivWriter.Write(aesProvider.IV);
}
// Create our encryptor and write the secret message to the encryptor stream.
var encryptor = aesProvider.CreateEncryptor(saltKey, aesProvider.IV);
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(secretMessage, 0, secretMessage.Length);
cryptoStream.FlushFinalBlock();
}
// Get the non-secret payload, IV, payload and IV lengths and encrypted data back as an array of bytes.
encryptedValue = memoryStream.ToArray();
}
}
return encryptedValue;
}
public string EncryptMessageWithPassword(string secretMessage, string password, byte[] nonSecretPayLoad = null)
{
byte[] secreteMessageBytes = Encoding.UTF8.GetBytes(secretMessage);
byte[] encryptedMessage = this.EncryptMessageWithPassword(secreteMessageBytes, password, nonSecretPayLoad);
return Convert.ToBase64String(encryptedMessage);
}
private byte[] CreateSaltKeysFromPassword(string password, int nonSecretPayloadSize, out byte[] saltHash)
{
byte[] saltKey;
//Use Random Salt to prevent pre-generated weak password attacks.
using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize / SaltBlockSize, SaltIterations))
{
// Get a generated salt derived from the user password, hashed n-times where n = SaltIterations
saltHash = generator.Salt;
//Generate Keys
saltKey = generator.GetBytes(KeyBitSize / SaltBlockSize);
}
return saltKey;
}
}
I would expect in my GetNonSecretPayload(byte[] payload); that by setting the position, or using binaryReader.BaseStream.Seek(2); to skip the IV length item, I would skip the IV size entry in the byte[] array and be able to read the bytes associated with the actual non-secret data. This doesn't work though, presumably because this isn't an array underneath the covers that I can just move to the next element in the array, skipping the IV length wrote out originally.
I have the following unit test.
[TestClass]
public class EncryptionTests
{
private const string _ContentToEncrypt = "This is a test to make sure the encryption Type actually encrypts the data right.";
private const string _Password = "EncryptedPassword1";
[TestMethod]
public void Extract_non_secret_payload_content_from_encrypted_string()
{
// Arrange
var encryption = new Encryption();
string nonSecretData = "My payload is not considered secret and can be pulled out of the payload without decrypting";
// Convert the secret and non-secret data into a byte array
byte[] payload = Encoding.UTF8.GetBytes(nonSecretData);
byte[] encodedBytes = Encoding.UTF8.GetBytes(_ContentToEncrypt);
// Encrypt the secret data while injecting the nonsecret payload into the encrypted stream.
byte[] encryptedValue = encryption.EncryptMessageWithPassword(encodedBytes, _Password, payload);
// Act
// Pull the non-secret payload out of the encrypted message - without having to decrypt it.
byte[] UnencryptedPayloadWithinEncryptedArray = encryption.GetNonSecretPayload(encryptedValue);
string payloadContent = Encoding.UTF8.GetString(UnencryptedPayloadWithinEncryptedArray);
// Assert
Assert.AreEqual(nonSecretData, payloadContent);
}
}
What I get with my current binaryReader.BaseStream.Position = 3 is
"\0\u0010\0\0\0My payload is not considered secret and can be pulled out of the payload without decry"
I've read and wrote data like this in the past using a BinaryWriter, but I've never had to seek through it in order to skip data. What am I doing wrong here?
I have to request data from an external existing webservice written in C#.
This webservice requires some of the data to be encrypted (The connection uses an SSL connection, some of the data is aes encrypted)
On the php site openssl is used for decrypting.
The following settings are used on the c# site
(This are the default values for the AesCryptoServiceProvider):
Algorithm: AES
Padding: PKCS7
Mode: CBC
Keysize: 256
The padding for PKCS7 works as following:
01 If 1 byte is missing
02 02 If 2 bytes are missing
and so on
so this values are not added by the padding.
What am I doing wrong?
I've checked this with c#, php and ruby - the decrypted data starts with 255, 254
To reproduce use the following parameters:
data:1234567890123456
key: First1
salt(iv):Data
using System;
using System.Security.Cryptography;
using System.Text;
using System.IO;
namespace crypto_test
{
class MainClass
{
public static void Main(string[] args)
{
bool running = true;
while (running)
{
Console.WriteLine("Enter data:");
var data = Console.ReadLine();
Console.WriteLine("Enter key:");
var key = Console.ReadLine();
Console.WriteLine("Enter iv:");
var iv = Console.ReadLine();
Console.WriteLine("Enter d for decode");
var decode = (Console.ReadLine() == "d");
string encoded=Crypt(data, key, iv, decode);
Console.WriteLine(encoded);
if (!decode)
{
encoded= Crypt(encoded, key, iv, true);
Console.WriteLine(encoded);
}
Console.WriteLine("quit to exit");
running = !(Console.ReadLine() == "quit");
}
}
public static string Crypt(string value, string password, string salt, bool decrypt)
{
DeriveBytes rgb = new Rfc2898DeriveBytes(password, Encoding.Unicode.GetBytes(salt));
SymmetricAlgorithm algorithm = new AesCryptoServiceProvider();
byte[] rgbKey = rgb.GetBytes(algorithm.KeySize >> 3);
byte[] rgbIV = rgb.GetBytes(algorithm.BlockSize >> 3);
Console.WriteLine("rbKey: size:{0} key:{1}", (algorithm.KeySize >> 3), GetHex(rgbKey));
Console.WriteLine("rgbIV: size:{0} key:{1}", (algorithm.BlockSize >> 3), GetHex(rgbIV));
ICryptoTransform transform = decrypt ? algorithm.CreateDecryptor(rgbKey, rgbIV) : algorithm.CreateEncryptor(rgbKey, rgbIV);
Console.WriteLine("Mode {0}", algorithm.Mode);
Console.WriteLine("PAdding {0}", algorithm.Padding);
using (MemoryStream buffer = new MemoryStream())
{
using (CryptoStream stream = new CryptoStream(buffer, transform, CryptoStreamMode.Write))
{
try
{
if (decrypt)
{
byte[] data = Convert.FromBase64String(value);
stream.Write(data,0,data.Length);
}
else
{
using (StreamWriter writer = new StreamWriter(stream, Encoding.Unicode))
{
writer.Write(value);
}
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
byte[] buff = buffer.ToArray();
if (decrypt)
{
return Encoding.Unicode.GetString(buff) + "\r\n" + GetHex(buff);
}
else
return Convert.ToBase64String(buff);
}
}
public static string GetHex(byte[] data)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.Length; ++i)
sb.Append(data[i].ToString("X2"));
return sb.ToString();
}
}
}
I have not found an equivalent to Rfc2898DeriveBytes until now,
so I copied the key and iv
php
<?php
$salt='Data';
$pass='First1';
$data='1234567890123456';
$encrypted_base64='VKNd9Pi+cttaM6ne8pzAuFbH1U0gJiJ2Wlbbr1rU5z8vbIfAS6nb0/5py4p54aK7';
$encrypted=base64_decode($encrypted_base64);
$key = pack('H*', "30EE7F95F0EF4835F048A481424F2F52EE21B7CEB97F8CC437E5949DB53797D9");
$iv = pack('H*', "B29F5ECF7057065758102385509F0637");
$cipher='AES-256-CBC';
$decrypted = openssl_decrypt($encrypted,$cipher, $key,true,$iv);
for($i =0; $i<strlen($decrypted);++$i)
{
echo "char:" . ord($decrypted[$i])."<br/>";
}
echo $decrypted
?>
ruby:
require ('openssl')
require ('base64')
while true
enc_data='VKNd9Pi+cttaM6ne8pzAuFbH1U0gJiJ2Wlbbr1rU5z8vbIfAS6nb0/5py4p54aK7'
data = Base64.decode64(enc_data)
key_hex='30EE7F95F0EF4835F048A481424F2F52EE21B7CEB97F8CC437E5949DB53797D9'
iv_hex='B29F5ECF7057065758102385509F0637'
key = [key_hex].pack('H*')
iv = [iv_hex].pack('H*')
decipher = OpenSSL::Cipher::AES.new(256, :CBC)
decipher.decrypt
decipher.key = key
decipher.iv = iv
plain = decipher.update(data) + decipher.final
puts plain
puts plain.bytes
end
Good news, your decryption seems to work OK.
What you are seeing in the decrypted ciphertext is the byte order mark for UTF-16 LE, which is (incorrectly) indicated by Microsoft as Encoding.Unicode. You need to do either one off two things:
decode the text with a decoder that groks UTF-16 LE including byte order mark;
encode using much more reasonable UTF-8 encoding (in the C# code).
Personally I would put a strong preference on (2).
I am using C# in a Visual Studio Windows form application to write a program that can encrypt and decrypt files. I am following this walkthrough: https://msdn.microsoft.com/en-US/library/Aa964697(v=VS.85).aspx and have everything completed with some minor changes made to my environment and preferences.
When I try to encrypt a file I am given a 'FileNotFoundException was unhandled' error when the program tries to use a filestream to encrypt the file. Everything up to that point seems to be working.
Here is the code for the EncryptFile method:
private void EncryptFile(string inFile)
{
// Create an instance of Rijndael for symmetric encryption of the data.
RijndaelManaged rjndl = new RijndaelManaged();
rjndl.KeySize = 256;
rjndl.BlockSize = 256;
rjndl.Mode = CipherMode.CBC;
ICryptoTransform transform = rjndl.CreateEncryptor();
// Use RSACryptoServiceProvider to enrypt the Rijndael key.
byte[] keyEncrypted = rsa.Encrypt(rjndl.Key, false);
// Create byte arrays to contain the length values of the key and IV.
byte[] LenK = new byte[4];
byte[] LenIV = new byte[4];
int lKey = keyEncrypted.Length;
LenK = BitConverter.GetBytes(lKey);
int lIV = rjndl.IV.Length;
LenIV = BitConverter.GetBytes(lIV);
// Write the following to the FileStream for the encrypted file (outFs):
// - length of the key
// - length of the IV
// - ecrypted key
// - the IV
// - the encrypted cipher content
// Change the file's extension to ".enc"
string outFile = EncrFolder + inFile.Substring(0, inFile.LastIndexOf(".")) + ".enc";
using (FileStream outFs = new FileStream(outFile, FileMode.Create))
{
outFs.Write(LenK, 0, 4);
outFs.Write(LenIV, 0, 4);
outFs.Write(keyEncrypted, 0, lKey);
outFs.Write(rjndl.IV, 0, lIV);
// Now write the cipher text using a CryptoStream for encrypting.
using (CryptoStream outStreamEncrypted = new CryptoStream(outFs, transform, CryptoStreamMode.Write))
{
// By encrypting a chunk at a time, you can save memory and accommodate large files.
int count = 0;
int offset = 0;
// blockSizeBytes can be any arbitrary size.
int blockSizeBytes = rjndl.BlockSize / 8;
byte[] data = new byte[blockSizeBytes];
int bytesRead = 0;
using (FileStream inFs = new FileStream(inFile, FileMode.Open))
{
do
{
count = inFs.Read(data, 0, blockSizeBytes);
offset += count;
outStreamEncrypted.Write(data, 0, count);
bytesRead += blockSizeBytes;
}
while (count > 0);
inFs.Close();
}
outStreamEncrypted.FlushFinalBlock();
outStreamEncrypted.Close();
}
outFs.Close();
}
}
The error happens at the line "using (FileStream inFs = new FileStream(inFile, FileMode.Open))".
Here is an image of the error:
What is causing the error and what is the fix?
you may overthink this line :
string outFile = EncrFolder + inFile.Substring(0, inFile.LastIndexOf(".")) + ".enc";
(https://msdn.microsoft.com/en-us/library/fyy7a5kt(v=vs.110).aspx)
I want to decrypt an already encrypted file but getting the error "Bad Data". I think that that the method I used here to generate key is not generating same key while encrypting and decrypting. So, I want to declare my own key. How can i do this?
This is my code.
using System;
using System.IO;
using System.Security;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
public static class crypto
{
public static String tempdir = Environment.ExpandEnvironmentVariables("%temp%");
// Call this function to remove the key from memory after use for security
[System.Runtime.InteropServices.DllImport("KERNEL32.DLL", EntryPoint = "RtlZeroMemory")]
public static extern bool ZeroMemory(IntPtr Destination, int Length);
// Function to Generate a 64 bits Key.
static string GenerateKey()
{
// Create an instance of Symetric Algorithm. Key and IV is generated automatically.
DESCryptoServiceProvider desCrypto = (DESCryptoServiceProvider)DESCryptoServiceProvider.Create();
Clipboard.SetText(desCrypto.Key.ToString());
// Use the Automatically generated key for Encryption.
return ASCIIEncoding.ASCII.GetString(desCrypto.Key);
}
static void EncryptFile(string sInputFilename,
string sOutputFilename,
string sKey)
{
FileStream fsInput = new FileStream(sInputFilename,
FileMode.Open,
FileAccess.Read);
FileStream fsEncrypted = new FileStream(sOutputFilename,
FileMode.Create,
FileAccess.Write);
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
ICryptoTransform desencrypt = DES.CreateEncryptor();
CryptoStream cryptostream = new CryptoStream(fsEncrypted,
desencrypt,
CryptoStreamMode.Write);
byte[] bytearrayinput = new byte[fsInput.Length];
fsInput.Read(bytearrayinput, 0, bytearrayinput.Length);
cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length);
cryptostream.Close();
fsInput.Close();
fsEncrypted.Close();
}
static void DecryptFile(string sInputFilename,
string sOutputFilename,
string sKey)
{
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
//A 64 bit key and IV is required for this provider.
//Set secret key For DES algorithm.
DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
//Set initialization vector.
DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
//Create a file stream to read the encrypted file back.
FileStream fsread = new FileStream(sInputFilename,
FileMode.Open,
FileAccess.Read);
//Create a DES decryptor from the DES instance.
ICryptoTransform desdecrypt = DES.CreateDecryptor();
//Create crypto stream set to read and do a
//DES decryption transform on incoming bytes.
CryptoStream cryptostreamDecr = new CryptoStream(fsread,
desdecrypt,
CryptoStreamMode.Read);
//Print the contents of the decrypted file.
StreamWriter fsDecrypted = new StreamWriter(sOutputFilename);
fsDecrypted.Write(new StreamReader(cryptostreamDecr).ReadToEnd());
fsDecrypted.Flush();
fsDecrypted.Close();
}
public static void Encrypt(String table, String file)
{
GenerateKey();
try
{
String filepath = Path.Combine(tempdir + #"\Manager\data\" + table,file);
// Must be 64 bits, 8 bytes.
// Distribute this key to the user who will decrypt this file.
string sSecretKey;
// Get the Key for the file to Encrypt.
sSecretKey = GenerateKey();
// For additional security Pin the key.
GCHandle gch = GCHandle.Alloc(sSecretKey, GCHandleType.Pinned);
// Encrypt the file.
EncryptFile(#"" + filepath,
#"" + Application.StartupPath + #"\data\"+ table + #"\" + file,
sSecretKey);
try
{
File.Delete(filepath);
}
catch (Exception ex1)
{
MessageBox.Show(ex1.Message, "Error while deletion of decrypted file");
}
// Remove the Key from memory.
ZeroMemory(gch.AddrOfPinnedObject(), sSecretKey.Length * 2);
gch.Free();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error during encryption");
}
}
public static void Decrypt(String table, String file)
{
try
{
String filepath = Path.Combine(tempdir + #"\Manager\data\"+table, file);
create.folder("Manager", tempdir);
create.folder(table, tempdir + #"\Manager\");
create.file(file, tempdir + #"\Manager\" + table);
// Must be 64 bits, 8 bytes.
// Distribute this key to the user who will decrypt this file.
string sSecretKey;
// Get the Key for the file to Encrypt.
sSecretKey = GenerateKey();
// For additional security Pin the key.
GCHandle gch = GCHandle.Alloc(sSecretKey, GCHandleType.Pinned);
// Decrypt the file.
DecryptFile(#".\data\" + table + #"\" + file,
#"" + filepath,
sSecretKey);
// Remove the Key from memory.
ZeroMemory(gch.AddrOfPinnedObject(), sSecretKey.Length * 2);
gch.Free();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error during decryption");
}
}
}
Your GenerateKey Method generates a new (random) key every time you call it (like you already stated in the comments there). If you want to use a random key, store the generated key somewhere and use it for decryption and encryption.
You can use a own "password" for encryption by giving your encryption/decryption method a own string value. e.g.
crypto.EncryptFile(#"D:\Testing\brownfox.txt", #"D:\Testing\brownfox_enc.txt", "abcd1234");
crypto.DecryptFile(#"D:\Testing\brownfox_enc.txt", #"D:\Testing\brownfox_dec.txt", "abcd1234");
As you can see in the MSDN reference to "DES.Key", DES supports keys with a length of 64bit, so it's important to note, that the string must have 8 characters.