AES encryption in C Sharp and decryption in Android using PBKDF2WithHmacSHA1 - c#

I am using Android and I am trying to decrypt a message encrypted in a C Sharp Server.
Below is the code for the C# Cryptor, that uses 256 bit long Keys, 128 bit long IV, 5000 Iterations. It uses Rfc2898DeriveBytes Class, so that is the same as PBKDF2WithHmacSHA1 in Android.
The decrypt function of the C# Cryptor takes as its IV the (reversed) first 128 bits of the 256 bit long key.
namespace CompanyName.Framework.Encryption
{
internal class SymmetricCryptor : ISymmetricCryptor
{
internal static int KeyLengthInBytes = 32;
internal int Iterations = 5000;
#region Private Fields
// RijndaelManaged aes; old version
AesManaged aes;
int IVLength = KeyLengthInBytes >> 1;
#endregion Private Fields
#region Internal Constructors
internal SymmetricCryptor( )
{
aes = new AesManaged
{
Mode = CipherMode.CBC,
KeySize= KeyLengthInBytes<<3,
Padding = PaddingMode.PKCS7,
};
//aes.KeySize = KeyLengthInBytes << 3;
//aes.Padding = PaddingMode.Zeros; //PKCS7 can not be used with stream
}
#endregion Internal Constructors
#region Public Methods
public byte[] Decrypt(byte[] cryptedData, string password, IVMode ivmode)
{
using (MemoryStream ms = new MemoryStream(cryptedData))
{
using (MemoryStream data = new MemoryStream())
{
Decrypt(ms, data, password,ivmode);
return data.ToArray();
}
}
}
public void Encrypt(Stream data, Stream trgStream, string password, IVMode ivmode)
{
try
{
var key = GetKey(password);
var iv = (ivmode == IVMode.Auto)
?key.GetBytes(IVLength).Reverse().ToArray()
: new byte[IVLength];
var dc = aes.CreateEncryptor(key.GetBytes(KeyLengthInBytes), iv);
using (CryptoStream cryptor = new CryptoStream(trgStream, dc, CryptoStreamMode.Write))
{
data.CopyTo(cryptor);
cryptor.FlushFinalBlock();
cryptor.Close();
}
}
catch (Exception)
{
throw new InvalidOperationException("Invalid password.");
}
}
public void Decrypt(Stream cryptedData, Stream trgStream, string password, IVMode ivmode)
{
try
{
var key= GetKey(password);
var iv = (ivmode == IVMode.Auto)
? key.GetBytes(IVLength).Reverse().ToArray()
: new byte[IVLength];
var dc = aes.CreateDecryptor(key.GetBytes(KeyLengthInBytes),iv);
using (CryptoStream cryptor = new CryptoStream(cryptedData, dc, CryptoStreamMode.Read))
{
cryptor.CopyTo(trgStream);
cryptor.Close();
}
}
catch (Exception)
{
throw new InvalidOperationException("Invalid password.");
}
}
public byte[] Encrypt(byte[] data, string password, IVMode ivmode)
{
using (MemoryStream ms = new MemoryStream(data))
{
using (MemoryStream cData = new MemoryStream())
{
Encrypt(ms, cData, password,ivmode);
return cData.ToArray();
}
}
}
#endregion Public Methods
#region Private Methods
private Rfc2898DeriveBytes GetKey(string password)
{
try
{
var iv =
CompanyName.Framework.Cryptography.Digest.SHA1.Compute(password);
return new Rfc2898DeriveBytes(password, iv, Iterations);
}
catch (Exception)
{
throw;
}
}
#endregion Private Methods
}
}
My Android Cryptor, which tries to decrypt a message encrypted by the above C Sharp Cryptor looks like this, I tried to copy the Decrypt method of the C Sharp Cryptor:
public class Cryptor {
private static final String TRANSFORMATION = "AES/CBC/PKCS7Padding";
private static final String AES = "AES";
private static final String RANDOM_ALGO = "SHA1PRNG";
private static final int KEY_LENGTH_IN_BITS = 256;
private static final int IV_LENGTH = 16;
private static final int PBE_ITERATION_COUNT = 5000;
private static final int PBE_SALT_LENGTH_INT_BITS = 128;
private static final String PBE_ALGO = "PBKDF2WithHmacSHA1";
public static byte[] generateKeyFromPassword(String password, int Size) throws GeneralSecurityException {
byte[] salt = generateSalt();
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, PBE_ITERATION_COUNT, Size);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(PBE_ALGO);
byte[] data = keyFactory.generateSecret(keySpec).getEncoded();
return data;
}
private static byte[] generateSalt() throws GeneralSecurityException {
return randomBytes(PBE_SALT_LENGTH_INT_BITS);
}
private static byte[] randomBytes(int length) throws GeneralSecurityException {
SecureRandom random = SecureRandom.getInstance(RANDOM_ALGO);
byte[] b = new byte[length];
random.nextBytes(b);
return b;
}
public static byte[] decrypt(byte[] cipherText, String password) throws GeneralSecurityException {
byte[] keyBytes = generateKeyFromPassword(password, 256);
byte[] ivBytes = generateKeyFromPassword(password, 128);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
ivBytes = reverse(ivBytes);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, AES);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
byte[] decrypted = cipher.doFinal(cipherText);
return decrypted;
}
public static byte[] reverse(byte[] array) {
if (array == null) {
return null;
}
int i = 0;
int j = array.length - 1;
byte tmp;
while (j > i) {
tmp = array[j];
array[j] = array[i];
array[i] = tmp;
j--;
i++;
}
return array;
}
But it is not working, When do final is called I get a
javax.crypto.BadPaddingException: error:1e06b065:Cipher functions:EVP_DecryptFinal_ex:BAD_DECRYPT
Exception. I am not sure what I am doing wrong, because my Decrypt Method in Android is doing the exact same thing as the Decrypt Method in C Sharp: First I generate a Key from the password, which is shared by the Csharp Server and me. Then I generate a random 128 bit IV, reversing it is not necessary, but C Sharp implementation reverses it, so I do it as well. Can anyone tell me what I am doing wrong? Here is the context where I use the Cryptor:
//open the client channel, read and return the response as byte[]
Channel clientChannel = new Channel(serverAddress);
byte[] result = clientChannel.execute(serviceID.toString(), data);
//result[] is encrypted data. firstTen is the shared Password
byte[] decrypted = Cryptor.decrypt(result, firstTen);
Server returns the result as Base64 encrypted, before passing it for decryption I get the result[] array through:
It comes as a Base64 String. I get the result[] array through:
Base64.decode(result, Base64.NO_WRAP);

You need to generate random salt and IV on Server side and sent it with ciphreText to Android side. Android need to use exactly the same salt and IV to derive key for decryption that that was used to derive encryption key on Server side.

Related

Is it possible to run a PHP function in .NET?

I'm trying to decrypt AES-128 CBC strings in C# but with no success (tried every decrypt method I found on internet for this type of encryption).
The only way I got a result was decrypting those strings in PHP using this code:
<?php
$crypto_data = hex2bin($crypto_data_hex);
$real_data = openssl_decrypt($crypto_data,
'AES-128-CBC',
'23d854ce7364b4f7',
OPENSSL_RAW_DATA,
'23d854ce7364b4f7');
?>
So, is there a way to run this PHP code in .NET (using a PHP.dll library or something similar) ? Or is there a real equivalent method for this operation in .NET C# ?
Thank you
The question seems to boil down to writing C# function that is equivalent to PHP function.
Try this code:
public class Program
{
public static byte[] HexToBytes(string hex)
{
int numberChars = hex.Length;
byte[] bytes = new byte[numberChars / 2];
for (int i = 0; i < numberChars; i += 2)
{
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return bytes;
}
static string Decrypt(byte[] cipherText, byte[] key, byte[] iv)
{
string plaintext;
AesManaged aes = new AesManaged
{
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7,
BlockSize = 128,
Key = key,
IV = iv
};
using (aes)
{
ICryptoTransform decryptor = aes.CreateDecryptor();
using (MemoryStream ms = new MemoryStream(cipherText))
{
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (StreamReader reader = new StreamReader(cs))
{
plaintext = reader.ReadToEnd();
}
}
}
}
return plaintext;
}
public static void Main()
{
string crypto_data_hex = "00965fa56e761b11d37b887f98e6bcc2"; // paste $crypto_data_hex value here
string sKey = "23d854ce7364b4f7";
string sIv = "23d854ce7364b4f7";
byte[] encrypted = HexToBytes(crypto_data_hex);
byte[] key = Encoding.ASCII.GetBytes(sKey);
byte[] iv = Encoding.ASCII.GetBytes(sIv);
string decrypted = Decrypt(encrypted, key, iv);
Console.WriteLine(decrypted);
}
}

Decrypt an encrypted text

I got a textbox and a 'decrypt' button in my Windows Form Application where I put an encrypted string in there and try to decrypt it but the problem is this. First, I got this class code called DataEncryptor from a guy on this website:
public class DataEncryptor
{
TripleDESCryptoServiceProvider symm;
#region Factory
public DataEncryptor()
{
this.symm = new TripleDESCryptoServiceProvider();
this.symm.Padding = PaddingMode.PKCS7;
}
public DataEncryptor(TripleDESCryptoServiceProvider keys)
{
this.symm = keys;
}
public DataEncryptor(byte[] key, byte[] iv)
{
this.symm = new TripleDESCryptoServiceProvider();
this.symm.Padding = PaddingMode.PKCS7;
this.symm.Key = key;
this.symm.IV = iv;
}
#endregion
#region Properties
public TripleDESCryptoServiceProvider Algorithm
{
get { return symm; }
set { symm = value; }
}
public byte[] Key
{
get { return symm.Key; }
set { symm.Key = value; }
}
public byte[] IV
{
get { return symm.IV; }
set { symm.IV = value; }
}
#endregion
#region Crypto
public byte[] Encrypt(byte[] data) { return Encrypt(data, data.Length); }
public byte[] Encrypt(byte[] data, int length)
{
try
{
// Create a MemoryStream.
var ms = new MemoryStream();
// Create a CryptoStream using the MemoryStream
// and the passed key and initialization vector (IV).
var cs = new CryptoStream(ms,
symm.CreateEncryptor(symm.Key, symm.IV),
CryptoStreamMode.Write);
// Write the byte array to the crypto stream and flush it.
cs.Write(data, 0, length);
cs.FlushFinalBlock();
// Get an array of bytes from the
// MemoryStream that holds the
// encrypted data.
byte[] ret = ms.ToArray();
// Close the streams.
cs.Close();
ms.Close();
// Return the encrypted buffer.
return ret;
}
catch (CryptographicException ex)
{
Console.WriteLine("A cryptographic error occured: {0}", ex.Message);
}
return null;
}
public string EncryptString(string text)
{
return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(text)));
}
public byte[] Decrypt(byte[] data) { return Decrypt(data, data.Length); }
public byte[] Decrypt(byte[] data, int length)
{
try
{
// Create a new MemoryStream using the passed
// array of encrypted data.
MemoryStream ms = new MemoryStream(data);
// Create a CryptoStream using the MemoryStream
// and the passed key and initialization vector (IV).
CryptoStream cs = new CryptoStream(ms,
symm.CreateDecryptor(symm.Key, symm.IV),
CryptoStreamMode.Read);
// Create buffer to hold the decrypted data.
byte[] result = new byte[length];
// Read the decrypted data out of the crypto stream
// and place it into the temporary buffer.
cs.Read(result, 0, result.Length);
return result;
}
catch (CryptographicException ex)
{
Console.WriteLine("A cryptographic error occured: {0}", ex.Message);
}
return null;
}
public string DecryptString(string data)
{
return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(data))).TrimEnd('\0');
}
#endregion
}
And he gave the usage of it:
string message="A very secret message here.";
DataEncryptor keys=new DataEncryptor();
string encr=keys.EncryptString(message);
// later
string actual=keys.DecryptString(encr);
I copied his code and works at encrypting and decrypting:
//my code
private void proceedED(string data)
{
DataEncryptor key = new DataEncryptor();
string encr = key.EncryptString(data);
string actual = key.DecryptString(encr);
encryptedLabel.Text = encr;
decryptedLabel.Text = actual;
}
Then I created a method like this:
private void proceedDecrypt(string data)
{
DataEncryptor key = new DataEncryptor();
string decr = key.DecryptString(data);
decryptedData.Text = decr;
}
The problem is that it crashes when I submit and I don't know why.
I think it should be a true encrypted string because it's just a normal string.
How do I fix this?
Each instance of DataEncryptor generates new keys. You need to use the same keys which encrypted the string to decrypt. If this is done in the same process then keep a reference to DataEncryptor key. Otherwise you need to initialize using the DataEncryptor(byte[] key, byte[] iv) constructor.
Try code like this:
class Program
{
static void Main(string[] args)
{
string key, iv;
var plain="A very secret message.";
var cipher=EncryptString(plain, out key, out iv);
// Later ...
var message=DecryptString(cipher, key, iv);
}
public static string EncryptString(string plain, out string key, out string iv)
{
var crypto=new DataEncryptor();
iv=Convert.ToBase64String(crypto.IV);
key=Convert.ToBase64String(crypto.Key);
return crypto.EncryptString(plain);
}
public static string DecryptString(string cipher, string key, string iv)
{
var crypto=new DataEncryptor(
Convert.FromBase64String(key),
Convert.FromBase64String(iv));
return crypto.DecryptString(cipher);
}
}
You can use encryption and decription with System.Security.Cryptography
1) Set encryption decription key
2) Encrypt data with encryption key
3) Decrypt data with same encryption key
Please refer below link with Encryption and Decription example.
Encryption/Decryption Function in .NET using the TripleDESCryptoServiceProvider Class
you are creating new object in both function;
DataEncryptor key = new DataEncryptor();
That is the reason, for your error.
Just declare;
DataEncryptor key = new DataEncryptor();
Out side of your proceedED() and proceedDecrypt(), I mean make it public.
OR you can pass pass key as parameter to proceedDecrypt() and use it in that function.
Like;
DataEncryptor key = new DataEncryptor();
private void proceedED(string data)
{
string encr = key.EncryptString(data);
string actual = key.DecryptString(encr);
encryptedLabel.Text = encr;
decryptedLabel.Text = actual;
proceedDecrypt(encr);
}
private void proceedDecrypt(string data)
{
string decr = key.DecryptString(data);
decryptedData.Text = decr;
}
Hope it helps..!!!
Well I finally solved it...
I copied this code from https://social.msdn.microsoft.com/Forums/vstudio/en-US/d6a2836a-d587-4068-8630-94f4fb2a2aeb/encrypt-and-decrypt-a-string-in-c?forum=csharpgeneral
static readonly string PasswordHash = "P##Sw0rd";
static readonly string SaltKey = "S#LT&KEY";
static readonly string VIKey = "#1B2c3D4e5F6g7H8";
public static string Encrypt(string plainText)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
byte[] keyBytes = new Rfc2898DeriveBytes(PasswordHash, Encoding.ASCII.GetBytes(SaltKey)).GetBytes(256 / 8);
var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.Zeros };
var encryptor = symmetricKey.CreateEncryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey));
byte[] cipherTextBytes;
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
cipherTextBytes = memoryStream.ToArray();
cryptoStream.Close();
}
memoryStream.Close();
}
return Convert.ToBase64String(cipherTextBytes);
}
public static string Decrypt(string encryptedText)
{
byte[] cipherTextBytes = Convert.FromBase64String(encryptedText);
byte[] keyBytes = new Rfc2898DeriveBytes(PasswordHash, Encoding.ASCII.GetBytes(SaltKey)).GetBytes(256 / 8);
var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.None };
var decryptor = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey));
var memoryStream = new MemoryStream(cipherTextBytes);
var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
byte[] plainTextBytes = new byte[cipherTextBytes.Length];
int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount).TrimEnd("\0".ToCharArray());
}
and removed the DataEncryptor class

AES cbc padding encryption/decryption on cross platform (.net c# and codename one bouncy castle)

Encryption/Decryption won't work in cross platform.
I have used this link to encrypt/decrypt text using bouncy castle AES cipher within codename one.
AES Encryption/Decryption with Bouncycastle Example in J2ME
While from server side (.net) , i am using this link to implement same method.
http://zenu.wordpress.com/2011/09/21/aes-128bit-cross-platform-java-and-c-encryption-compatibility/
now i am not getting any error but encrypted from codename one will not getting fully decrypted on server side and vice a versa.
any one please help me out on this.
Code from Codename one:
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.encoders.Base64;
public class Test
{
private static PaddedBufferedBlockCipher cipher = null;
public static void main(String[] args)
{
try
{
byte key[] = "MAKV2SPBNI992122".getBytes("UTF-8");
byte[] iv = new byte[16];
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(
new AESEngine()) );
//Encryption
String plainText = "Hello How are you !2#&*()% 123456#";
byte[] plainData = plainText.getBytes("UTF-8");
KeyParameter keyParam = new KeyParameter(key);
CipherParameters ivAndKey = new ParametersWithIV(keyParam, iv);
cipher.init(true, ivAndKey);
byte[] ciptherBytes = cipherData(plainData); //48
String cipherText = new String(Base64.encode(ciptherBytes), "UTF-8");//FileUtil.getStringFromByteArray(Base64.encode(ciptherBytes));
System.out.println("encrypted >> "+cipherText);
//Decryption
byte[] cipherData = Base64.decode(cipherText);
ivAndKey = new ParametersWithIV(keyParam, iv);
cipher.init(false, ivAndKey);
plainText = new String(cipherData(cipherData), "UTF-8");//FileUtil.getStringFromByteArray(cipherData(cipherData));
System.out.println("decrypted >> "+plainText);
}
catch (Exception e)
{
e.printStackTrace();
}
}
private static byte[] cipherData(byte[] data)
throws CryptoException
{
int minSize = cipher.getOutputSize(data.length);
byte[] outBuf = new byte[minSize];
int length1 = cipher.processBytes(data, 0, data.length, outBuf, 0);
int length2 = cipher.doFinal(outBuf, length1);
int actualLength = length1 + length2;
byte[] result = new byte[actualLength];
System.arraycopy(outBuf, 0, result, 0, result.length);
return result;
}
Code from .net:
public static RijndaelManaged GetRijndaelManaged(String secretKey)
{
var keyBytes = new byte[16];
var secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
Array.Copy(secretKeyBytes, keyBytes, Math.Min(keyBytes.Length, secretKeyBytes.Length));
return new RijndaelManaged
{
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7,
KeySize = 128,
BlockSize = 128,
Key = keyBytes,
IV = keyBytes
};
}
public static byte[] EncryptCBC(byte[] plainBytes, RijndaelManaged rijndaelManaged)
{
return rijndaelManaged.CreateEncryptor()
.TransformFinalBlock(plainBytes, 0, plainBytes.Length);
}
public static byte[] DecryptCBC(byte[] encryptedData, RijndaelManaged rijndaelManaged)
{
return rijndaelManaged.CreateDecryptor()
.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
}
public static String EncryptCBCStr(String plainText, String key)
{
var plainBytes = Encoding.UTF8.GetBytes(plainText);
return Convert.ToBase64String(EncryptCBC(plainBytes, GetRijndaelManaged(key)));
}
public static String DecryptCBCStr(String encryptedText, String key)
{
var encryptedBytes = Convert.FromBase64String(encryptedText);
return Encoding.UTF8.GetString(DecryptCBC(encryptedBytes, GetRijndaelManaged(key)));
}
// call
var PlainText = "Hello How are you !2#&*()% 123456#";
var EncryptionKey = "MAKV2SPBNI992122";
var cypherCBC = EncryptCBCStr(PlainText, EncryptionKey);
var decryptCBC = DecryptCBCStr(cypherCBC, EncryptionKey);
Thanks in adv.
This issue has been fixed...it is just key/IV bytes issue.as in .net there is same key and IV when in java i have used different IV.
correction in java code:
instead of this
byte key[] = "MAKV2SPBNI992122".getBytes("UTF-8");
byte[] iv = new byte[16];
use this.
byte key[] = "MAKV2SPBNI992122".getBytes("UTF-8");
byte[] iv = "MAKV2SPBNI992122".getBytes("UTF-8");

C# RSA encryption/decryption with transmission

I've seen plenty of encryption/decryption tutorials and examples on the net in C# that use the System.Security.Cryptography.RSACryptoServiceProvider, but what I'm hoping to be able to do is:
Create an RSA public/private keypair
Transmit the public key (or for proof of concept, just move it in a string variable)
Create a new RSA crypto provider and encrypt a string with the public key
Transmit the encrypted string (or data) back to the original crypto provider and decrypt the string
Could anyone point me to a useful resource for this?
well there are really enough examples for this, but anyway, here you go
using System;
using System.Security.Cryptography;
namespace RsaCryptoExample
{
static class Program
{
static void Main()
{
//lets take a new CSP with a new 2048 bit rsa key pair
var csp = new RSACryptoServiceProvider(2048);
//how to get the private key
var privKey = csp.ExportParameters(true);
//and the public key ...
var pubKey = csp.ExportParameters(false);
//converting the public key into a string representation
string pubKeyString;
{
//we need some buffer
var sw = new System.IO.StringWriter();
//we need a serializer
var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
//serialize the key into the stream
xs.Serialize(sw, pubKey);
//get the string from the stream
pubKeyString = sw.ToString();
}
//converting it back
{
//get a stream from the string
var sr = new System.IO.StringReader(pubKeyString);
//we need a deserializer
var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
//get the object back from the stream
pubKey = (RSAParameters)xs.Deserialize(sr);
}
//conversion for the private key is no black magic either ... omitted
//we have a public key ... let's get a new csp and load that key
csp = new RSACryptoServiceProvider();
csp.ImportParameters(pubKey);
//we need some data to encrypt
var plainTextData = "foobar";
//for encryption, always handle bytes...
var bytesPlainTextData = System.Text.Encoding.Unicode.GetBytes(plainTextData);
//apply pkcs#1.5 padding and encrypt our data
var bytesCypherText = csp.Encrypt(bytesPlainTextData, false);
//we might want a string representation of our cypher text... base64 will do
var cypherText = Convert.ToBase64String(bytesCypherText);
/*
* some transmission / storage / retrieval
*
* and we want to decrypt our cypherText
*/
//first, get our bytes back from the base64 string ...
bytesCypherText = Convert.FromBase64String(cypherText);
//we want to decrypt, therefore we need a csp and load our private key
csp = new RSACryptoServiceProvider();
csp.ImportParameters(privKey);
//decrypt and strip pkcs#1.5 padding
bytesPlainTextData = csp.Decrypt(bytesCypherText, false);
//get our original plainText back...
plainTextData = System.Text.Encoding.Unicode.GetString(bytesPlainTextData);
}
}
}
as a side note: the calls to Encrypt() and Decrypt() have a bool parameter that switches between OAEP and PKCS#1.5 padding ... you might want to choose OAEP if it's available in your situation
public static string Encryption(string strText)
{
var publicKey = "<RSAKeyValue><Modulus>21wEnTU+mcD2w0Lfo1Gv4rtcSWsQJQTNa6gio05AOkV/Er9w3Y13Ddo5wGtjJ19402S71HUeN0vbKILLJdRSES5MHSdJPSVrOqdrll/vLXxDxWs/U0UT1c8u6k/Ogx9hTtZxYwoeYqdhDblof3E75d9n2F0Zvf6iTb4cI7j6fMs=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
var testData = Encoding.UTF8.GetBytes(strText);
using (var rsa = new RSACryptoServiceProvider(1024))
{
try
{
// client encrypting data with public key issued by server
rsa.FromXmlString(publicKey.ToString());
var encryptedData = rsa.Encrypt(testData, true);
var base64Encrypted = Convert.ToBase64String(encryptedData);
return base64Encrypted;
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
}
public static string Decryption(string strText)
{
var privateKey = "<RSAKeyValue><Modulus>21wEnTU+mcD2w0Lfo1Gv4rtcSWsQJQTNa6gio05AOkV/Er9w3Y13Ddo5wGtjJ19402S71HUeN0vbKILLJdRSES5MHSdJPSVrOqdrll/vLXxDxWs/U0UT1c8u6k/Ogx9hTtZxYwoeYqdhDblof3E75d9n2F0Zvf6iTb4cI7j6fMs=</Modulus><Exponent>AQAB</Exponent><P>/aULPE6jd5IkwtWXmReyMUhmI/nfwfkQSyl7tsg2PKdpcxk4mpPZUdEQhHQLvE84w2DhTyYkPHCtq/mMKE3MHw==</P><Q>3WV46X9Arg2l9cxb67KVlNVXyCqc/w+LWt/tbhLJvV2xCF/0rWKPsBJ9MC6cquaqNPxWWEav8RAVbmmGrJt51Q==</Q><DP>8TuZFgBMpBoQcGUoS2goB4st6aVq1FcG0hVgHhUI0GMAfYFNPmbDV3cY2IBt8Oj/uYJYhyhlaj5YTqmGTYbATQ==</DP><DQ>FIoVbZQgrAUYIHWVEYi/187zFd7eMct/Yi7kGBImJStMATrluDAspGkStCWe4zwDDmdam1XzfKnBUzz3AYxrAQ==</DQ><InverseQ>QPU3Tmt8nznSgYZ+5jUo9E0SfjiTu435ihANiHqqjasaUNvOHKumqzuBZ8NRtkUhS6dsOEb8A2ODvy7KswUxyA==</InverseQ><D>cgoRoAUpSVfHMdYXW9nA3dfX75dIamZnwPtFHq80ttagbIe4ToYYCcyUz5NElhiNQSESgS5uCgNWqWXt5PnPu4XmCXx6utco1UVH8HGLahzbAnSy6Cj3iUIQ7Gj+9gQ7PkC434HTtHazmxVgIR5l56ZjoQ8yGNCPZnsdYEmhJWk=</D></RSAKeyValue>";
var testData = Encoding.UTF8.GetBytes(strText);
using (var rsa = new RSACryptoServiceProvider(1024))
{
try
{
var base64Encrypted = strText;
// server decrypting data with private key
rsa.FromXmlString(privateKey);
var resultBytes = Convert.FromBase64String(base64Encrypted);
var decryptedBytes = rsa.Decrypt(resultBytes, true);
var decryptedData = Encoding.UTF8.GetString(decryptedBytes);
return decryptedData.ToString();
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
}
Honestly, I have difficulty implementing it because there's barely any tutorials I've searched that displays writing the keys into the files. The accepted answer was "fine". But for me I had to improve it so that both keys gets saved into two separate files. I've written a helper class so y'all just gotta copy and paste it. Hope this helps lol.
using Microsoft.Win32;
using System;
using System.IO;
using System.Security.Cryptography;
namespace RsaCryptoExample
{
class RSAFileHelper
{
readonly string pubKeyPath = "public.key";//change as needed
readonly string priKeyPath = "private.key";//change as needed
public void MakeKey()
{
//lets take a new CSP with a new 2048 bit rsa key pair
RSACryptoServiceProvider csp = new RSACryptoServiceProvider(2048);
//how to get the private key
RSAParameters privKey = csp.ExportParameters(true);
//and the public key ...
RSAParameters pubKey = csp.ExportParameters(false);
//converting the public key into a string representation
string pubKeyString;
{
//we need some buffer
var sw = new StringWriter();
//we need a serializer
var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
//serialize the key into the stream
xs.Serialize(sw, pubKey);
//get the string from the stream
pubKeyString = sw.ToString();
File.WriteAllText(pubKeyPath, pubKeyString);
}
string privKeyString;
{
//we need some buffer
var sw = new StringWriter();
//we need a serializer
var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
//serialize the key into the stream
xs.Serialize(sw, privKey);
//get the string from the stream
privKeyString = sw.ToString();
File.WriteAllText(priKeyPath, privKeyString);
}
}
public void EncryptFile(string filePath)
{
//converting the public key into a string representation
string pubKeyString;
{
using (StreamReader reader = new StreamReader(pubKeyPath)){pubKeyString = reader.ReadToEnd();}
}
//get a stream from the string
var sr = new StringReader(pubKeyString);
//we need a deserializer
var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
//get the object back from the stream
RSACryptoServiceProvider csp = new RSACryptoServiceProvider();
csp.ImportParameters((RSAParameters)xs.Deserialize(sr));
byte[] bytesPlainTextData = File.ReadAllBytes(filePath);
//apply pkcs#1.5 padding and encrypt our data
var bytesCipherText = csp.Encrypt(bytesPlainTextData, false);
//we might want a string representation of our cypher text... base64 will do
string encryptedText = Convert.ToBase64String(bytesCipherText);
File.WriteAllText(filePath,encryptedText);
}
public void DecryptFile(string filePath)
{
//we want to decrypt, therefore we need a csp and load our private key
RSACryptoServiceProvider csp = new RSACryptoServiceProvider();
string privKeyString;
{
privKeyString = File.ReadAllText(priKeyPath);
//get a stream from the string
var sr = new StringReader(privKeyString);
//we need a deserializer
var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
//get the object back from the stream
RSAParameters privKey = (RSAParameters)xs.Deserialize(sr);
csp.ImportParameters(privKey);
}
string encryptedText;
using (StreamReader reader = new StreamReader(filePath)) { encryptedText = reader.ReadToEnd(); }
byte[] bytesCipherText = Convert.FromBase64String(encryptedText);
//decrypt and strip pkcs#1.5 padding
byte[] bytesPlainTextData = csp.Decrypt(bytesCipherText, false);
//get our original plainText back...
File.WriteAllBytes(filePath, bytesPlainTextData);
}
}
}
I'll share my very simple code for sample purpose. Hope it will help someone like me searching for quick code reference.
My goal was to receive rsa signature from backend, then validate against input string using public key and store locally for future periodic verifications.
Here is main part used for signature verification:
...
var signature = Get(url); // base64_encoded signature received from server
var inputtext= "inputtext"; // this is main text signature was created for
bool result = VerifySignature(inputtext, signature);
...
private bool VerifySignature(string input, string signature)
{
var result = false;
using (var cps=new RSACryptoServiceProvider())
{
// converting input and signature to Bytes Arrays to pass to VerifyData rsa method to verify inputtext was signed using privatekey corresponding to public key we have below
byte[] inputtextBytes = Encoding.UTF8.GetBytes(input);
byte[] signatureBytes = Convert.FromBase64String(signature);
cps.FromXmlString("<RSAKeyValue><Modulus>....</Modulus><Exponent>....</Exponent></RSAKeyValue>"); // xml formatted publickey
result = cps.VerifyData(inputtextBytes , new SHA1CryptoServiceProvider(), signatureBytes );
}
return result;
}
for big data
public class RsaService : System.IDisposable
{
public delegate int TransformBlockCall(System.ReadOnlySpan<byte> data, System.Span<byte> destination);
private readonly RSA _encoder;
private readonly RSAEncryptionPadding _padding;
private readonly TransformBlockCall _encryptBlockCall;
private readonly TransformBlockCall _decryptBlockCall;
private int _encrypt_InputBlockSize;
private int _encrypt_OutputBlockSize;
private int _decrypt_InputBlockSize;
private int _decrypt_OutputBlockSize;
public RsaService(RSA encoder) {
if(encoder == null)
throw new System.ArgumentNullException(nameof(encoder));
_encoder = encoder;
_padding = RSAEncryptionPadding.Pkcs1;
_encryptBlockCall = new TransformBlockCall(EncryptBlock);
_decryptBlockCall = new TransformBlockCall(DecryptBlock);
OnEndSetParameters();
}
private void OnEndSetParameters() {
_encrypt_InputBlockSize = GetSizeOutputEncryptOfKeySize(_encoder.KeySize);
_encrypt_OutputBlockSize = _encoder.KeySize / 8;
_decrypt_InputBlockSize = _encrypt_OutputBlockSize;
_decrypt_OutputBlockSize = _encrypt_OutputBlockSize;
}
public void ImportParameters(RSAParameters parameters) {
_encoder.ImportParameters(parameters);
OnEndSetParameters();
}
public byte[] Encrypt(byte[] data) {
if(data == null) throw new System.ArgumentNullException(nameof(data));
if(data.Length == 0) return data;
int outputLength = GetEncryptOutputMaxByteCount(data.Length);
byte[] outputData = new byte[outputLength];
Encrypt(data, outputData);
return outputData;
}
public byte[] Decrypt(byte[] data) {
if(data == null) throw new System.ArgumentNullException(nameof(data));
if(data.Length == 0) return data;
int maxOutputLength = GetDecryptOutputMaxByteCount(data.Length);
byte[] outputData = new byte[maxOutputLength];
int actual_OutputLength = Decrypt(data, outputData);
if(maxOutputLength > actual_OutputLength)
System.Array.Resize(ref outputData, actual_OutputLength);
return outputData;
}
public int Encrypt(System.ReadOnlySpan<byte> data, System.Span<byte> destination) {
#if DEBUG
int inputBlockSize = _encrypt_InputBlockSize;
int outputBlockSize = _encoder.KeySize / 8;
int blockCount = (data.Length / inputBlockSize);
if(data.Length % inputBlockSize != 0)
blockCount++;
System.Diagnostics.Debug.Assert((blockCount * outputBlockSize) <= destination.Length);
#endif
if(data.Length > _encrypt_InputBlockSize)
return TransformFinal(_encryptBlockCall, data, destination, _encrypt_InputBlockSize);
else
return _encryptBlockCall(data, destination);
}
public int Decrypt(System.ReadOnlySpan<byte> data, System.Span<byte> destination) {
if(data.Length > _decrypt_InputBlockSize)
return TransformFinal(_decryptBlockCall, data, destination, _decrypt_InputBlockSize);
else
return _decryptBlockCall(data, destination);
}
private int EncryptBlock(System.ReadOnlySpan<byte> data, System.Span<byte> destination) => _encoder.Encrypt(data, destination, _padding);
private int DecryptBlock(System.ReadOnlySpan<byte> data, System.Span<byte> destination) => _encoder.Decrypt(data, destination, _padding);
public int GetEncryptOutputMaxByteCount(int inputCount) => GetBlockCount(inputCount, _encrypt_InputBlockSize) * _encrypt_OutputBlockSize;
public int GetDecryptOutputMaxByteCount(int inputCount) => GetBlockCount(inputCount, _decrypt_InputBlockSize) * _decrypt_OutputBlockSize;
public void Dispose() {
_encoder.Dispose();
System.GC.SuppressFinalize(this);
}
#region Methods_Helper
public static RsaService Create(RSAParameters parameters) => new RsaService(RSA.Create(parameters));
public static RsaService Create() => new RsaService(RSA.Create());
// [keySize] ÷ 8 - [11 bytes for padding] = Result
// Exsimple: [2048 key size] ÷ 8 - [11 bytes for padding] = 245
public static int GetSizeOutputEncryptOfKeySize(int keySize) => (keySize / 8) - 11;
private static int GetBlockCount(int dataLength,int inputBlockSize) {
int blockCount = (dataLength / inputBlockSize);
if(dataLength % inputBlockSize != 0)
blockCount++;
return blockCount;
}
public static int TransformFinal(TransformBlockCall transformBlockCall, System.ReadOnlySpan<byte> data, System.Span<byte> destination, int inputBlockSize) {
int blockCount = GetBlockCount(data.Length, inputBlockSize);
int data_writtenCount = 0;
int destination_writtenCount = 0;
while(blockCount-- > 0) {
if(blockCount == 0) {
inputBlockSize = data.Length - data_writtenCount;
if(inputBlockSize == 0) break;
}
destination_writtenCount += transformBlockCall(data: data.Slice(data_writtenCount, inputBlockSize)
, destination: destination.Slice(destination_writtenCount));
data_writtenCount += inputBlockSize;
}
return destination_writtenCount;
}
public static (RSAParameters keyPublic, RSAParameters keyPrivate) GenerateKeyPair(int keySize = 2048) {
RSAParameters keyPriv;
RSAParameters keyPub;
using(var rsa = RSA.Create(keySize)) {
keyPriv = rsa.ExportParameters(true);
keyPub = rsa.ExportParameters(false);
}
return (keyPub, keyPriv);
}
#endregion Methods_Helper
}
public static class Program
{
static void Main() {
var (keyPublic, keyPrivate) = RsaService.GenerateKeyPair();
var encryptor = RsaService.Create(keyPublic);
var decryptor = RsaService.Create(keyPrivate);
string originalText = "";
for(int i = 0; i < 1000; i++) {
originalText += "ABC123456789";
}
byte[] inputData = Encoding.UTF8.GetBytes(originalText); // data random for test
System.Console.WriteLine("inputData.Length: {0}", inputData.Length);
var encryptedData = encryptor.Encrypt(inputData);
System.Console.WriteLine("encryptedData.Length: {0}", encryptedData.Length);
byte[] decryptedData = decryptor.Decrypt(encryptedData);
string decryptedText = Encoding.UTF8.GetString(decryptedData);
System.Console.WriteLine("status: {0}", decryptedText == originalText);
}
}

C# RSA public Modulus/Exponent? (Bad Data)

I have tried and tried but I continue to get "Bad Data". How do you decrypt data using the RSACryptoServiceProvider with the public key's Exponent/Modulus?
public static byte[] Encrypt(byte[] b, byte[] mod, byte[] exp)
{
CspParameters csp = new CspParameters();
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);
RSACryptoServiceProvider.UseMachineKeyStore = false;
RSAParameters par = new RSAParameters();
par.Exponent = exp;
par.Modulus = mod;
rsa.ImportParameters(par);
return rsa.Encrypt(b, false);
}
public static byte[] Decrypt(byte[] b, byte[] pubexp, byte[] mod, byte[] priexp)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
RSACryptoServiceProvider.UseMachineKeyStore = false;
RSAParameters rp = new RSAParameters();
rp.Exponent = pubexp;
rp.D = priexp;
rp.Modulus = mod;
rsa.ImportParameters(rp);
return rsa.Decrypt(b, false);
}
static List<byte[]> Base2Array(string str)
{
byte[] b = Convert.FromBase64String(str);
List<byte[]> Bytes = new List<byte[]>();
int i = 0;
while (i < b.Length)
{
int size = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(b, i));
i += 4;
byte[] b2 = new byte[size];
Array.Copy(b, i, b2, 0, size);
Bytes.Add(b2);
i += size;
}
return Bytes;
}
static void Main(string[] args)
{
List<byte[]> pub = Base2Array("AAAAB3NzaC1yc2EAAAABJQAAAIBMW4HxU1glv+CcZpJnvUKEyeNfFoKkyLOVLOOb/vNXQkrkGsNdpYAZkKKizij8fD3u3/iYT8UI+xkFoyonRYVipgCslirJB1VdvLivXs69Ht4vf7VAv2yJSUni3XsIHauMlfOkjJ7DpUW75ZkrxsGieICFWlXvRnAyDdqQrkZRZQ==");
List<byte[]> pri = Base2Array("AAAAgBSjHDNiojO3UXZg6Ux4VyrOx9SCn9mCWgykWTEUeR6Anp6DxhlPUw3UEEetVy97hlw8iGCEQxcvG4T7qocni9UtUTLdpuQzvr6718y2CP0ouKt/1hVKD9QssT08XUvJEBQnnl2yVZAbIqT/DGnUH36L0BnQE/2ombPakwHscfFFAAAAQQCSfQy2cP8Oa0IR0u0whxqGmuuXY/3tCD8NaaSCYm31ly0QBdxFdf2WkC51DNVaf5/1ErHceMapFN9Z3j+/6lA7AAAAQQCFcMoSA32f240nFBmMv/nn/mL1uSdAXOxjUHViJc6M8ntZvW2ZuP2qTvfA3mh1AK5K69piX/4T72xxqTA2tmrfAAAAQFxX1JunFI+fpobdienVCZcjibwbpDPf1MVTbwQhAXHqVBL3XXgkysS/67X/aWnv/+SdBDaXa1SnDpphSWOkxAQ=");
//pub[0] 7
//pub[1] 1
//pub[2] 128
//pri[0] 128
//pri[1] 65
//pri[2] 65
//pri[3] 64
byte[] pubmod = null;
byte[] primod = null;
byte[] pubexp = null;
byte[] priexp = null;
pubexp = pub[0];
pubmod = pub[2];
priexp = pri[0];
primod = pri[2];
byte[] bstr = Encoding.ASCII.GetBytes("Test");
bstr = Encrypt(bstr, pubmod, pubexp);
bstr = Decrypt(bstr, pubexp, pubmod, null);
string str = Encoding.ASCII.GetString(bstr);
}
i do something like that:
public byte[] PublicDecryption(byte[] encryptedData)
{
var encData = new BigInteger(encryptedData);
BigInteger bnData = encData.modPow(_exponent, _modulus);
return bnData.getBytes();
}
public byte[] PrivateDecryption(byte[] encryptedData)
{
var encData = new BigInteger(encryptedData);
d = new BigInteger(rsaParams.D);
BigInteger bnData = encData.modPow(d, _modulus);
return bnData.getBytes();
}
where BigInteger is this:
http://www.codeproject.com/KB/cs/biginteger.aspx
because microsoft implementation is partial and buggy.
I've never had a problem with this.
Hope this help
Few month ago I have tried to implement scenario with private key encryption and public key decryption. I spent a week trying doing this with RSACryptoServiceProvider and.. nothing. They support only two use cases:
Public key encryption , private (full) key decryption.
Signing with private key, verifying with public.
And they have done everything to not allow you do something else with their API.
Please, check out msdn forums:
I have found many answers like that one msdn, including answers from support development team. All they said: that this is prohibit by design. So my suggestion is don't even try to do that with RSACryptoServiceProvider, better use another implementation.

Categories

Resources