Invalid Cipher Text Exception while decrypting AES key using RSA - c#

I'm working on encrypt/decrypt request/response on both angular 6 and .net core api side. I'm encrypting data in angular using AES and encrypting AES key using RSA public key and sending it to .net core api. I have created action filter to decrypt the request, and to decrypt the request first I have to decrypt the AES key using RSA private key but it is giving me an error while decrypting AES key using RSA private key:
ex {Org.BouncyCastle.Crypto.InvalidCipherTextException: block incorrect
at Org.BouncyCastle.Crypto.Encodings.Pkcs1Encoding.DecodeBlock(Byte[] input, Int32 inOff, Int32 inLen)
at Phyzii.Core.Api.Security.RSA.Decrypt(String cipherText)} System.Exception {Org.BouncyCastle.Crypto.InvalidCipherTextException}
Here is my RSA encryption code at angular side:
import JSEncrypt from 'jsencrypt';
encryptObj = new JSEncrypt();
transitionIn(data: any) {
this.aesSecretKey=this.makeUniqueKey(10);
console.log(this.aesSecretKey);
this.data.DATAOBJ = this.aesEncrypt(this.aesSecretKey, data);
this.encryptObj.setPublicKey(this.publicKeyClient);
this.data.KEY = this.encryptObj.encrypt(this.aesSecretKey);
return this.data;
}
Here is my RSA decryption code at C# side:
private static AsymmetricCipherKeyPair ReadPemFile(string flag)
{
string filePath = flag == "PUBLIC" ? "D:/Crypto/private_key.pem" : "D:/Crypto/private_key_server.pem";
AsymmetricCipherKeyPair keys;
using (var reader = File.OpenText(filePath))// file containing RSA PKCS1 private key
keys = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();
AsymmetricKeyParameter private_key = keys.Private;
AsymmetricKeyParameter public_key = keys.Public;
return keys;
}
//cipherText = "gOItOryuGy0UXHfoNqo0omcXLIOS6dhLJas5zeDNA7MfvsHYwP4ccSWU9JwTrIRiYUq/NB9oRn62ZQ5ynDnsGXUmHfVT4oPxtQZE1fXTTMN5ycfgthegesmXoZMMcWxA/wnwjLAgE17MNaunKY307W+nyc3jEMT1QsWUoOBESo0="
public static string Decrypt(string cipherText)
{
try
{
byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
AsymmetricCipherKeyPair keys = ReadPemFile("PRIVATE");
AsymmetricKeyParameter private_key = keys.Private;
// Pure mathematical RSA implementation
// RsaEngine eng = new RsaEngine();
// PKCS1 v1.5 paddings
// Pkcs1Encoding eng = new Pkcs1Encoding(new RsaEngine());
// PKCS1 OAEP paddings
Pkcs1Encoding eng = new Pkcs1Encoding(new RsaEngine());
eng.Init(false, private_key);
int length = cipherTextBytes.Length;
int blockSize = eng.GetInputBlockSize();
List<byte> plainTextBytes = new List<byte>();
for (int chunkPosition = 0; chunkPosition < length; chunkPosition += blockSize)
{
int chunkSize = Math.Min(blockSize, length - chunkPosition);
plainTextBytes.AddRange(eng.ProcessBlock(cipherTextBytes, chunkPosition, chunkSize));
}
return Encoding.UTF8.GetString(plainTextBytes.ToArray());
}
catch (Exception ex)
{
throw ex;
}
}
and If i set true in Pkcs1Encoding Init
Pkcs1Encoding eng = new Pkcs1Encoding(new RsaEngine());
eng.Init(true, private_key);
Then it is decrypting data in this format �����Ŧ���%�Rc��\u000e�\b\u0004����I�]&P~�+�뛡�^s�V�ʗ' \b��?Jv�F�ge\u001b�S���^�\u0002��v/|�vh�}�z�[A�}��\u0002u\\�Pp����\u0011k9\u001e\n�E\b�\u0003��\u001a#��}��y��\u000eTG�U\a�A_KV�\u007fs����?3���*/*\n\n~�w�Q��'��\a:���q��BH\u0004R�#c��'d�\u001f���\0 5\u007f���fs�<���\u0012\t����|�[\u0015\b+��8\u0003�[�v����Ǹ��8ځ\u001cԞv�{=���#-o\u0004�I\u0014J�\0#4`
which is incorrect,
what can be the issue?

Related

C# AES-GCM and RSA Encryption using Sytem.Secuirty.Cryptography

I am developing the post data request in c# and using AES GCM encryption and RSA encryption. As compared to the C# code with Node.js / Python for encryption, we found that C# AES GCM encryption does not have tag value in the method.
e.g How do I return below data in C# after encryption.
return {
key: aesKey,
cipher_text: encrypted,
tag,
iv
}
In Python and Node.js using JsEncrypt, forge.js.... and implementations are working fine. But, now developing the same client in the C#, do not find such libraries which I can use directly.
However, I found two classes in C# AesGcm and AesManaged for AES Encryption. But I think AesManages is only for AES not GCM encryption. Therefore, I am using AesGcm class in below example, but not sure.
The code executes the following steps:
Request client certificate from DSG.
Extract the public key from the certificate.
Encrypt PII with AES key.
Encrypt AES key with RSA public key.
Send wrapped key and PII payload to DSG tokenization.
class Program
{
static void Main(string[] args)
{
string _ApiKey = "--- YOUR API KEY ----";
string _SecretKey = " ---- YOUR SECRET KEY --------";
string jsonPlainText = "<JSON TExT>";
#region Certificate
string certificate = GetCertificate();
Certificate rawSigningCert = JsonConvert.DescrializeObject<Certificate>(certificate);
var certBytes = Encoding.UTF8.GetBytes(rawSigningCert.crt);
// X509Certificate2
var x509Certificate2 = new System.Security.Cryptography.X509Certificates.X509Certificate2(certBytes);
byte[] publicKey = x509Certificate2.GetPublicKey();
#endregion
#region AES GCM Encryption
byte[] aesGcmKey = new byte[16];
RandomNumberGenerator.Fill(aesGcmKey);
byte[] nonce = new byte[12];
RandomNumberGenerator.Fill(nonce);
byte[] tag = new byte[16];
byte[] dataToEncrypt = Encoding.UTF8.GetBytes(jsonPlainText);
byte[] cipherText = new byte[dataToEncrypt.Length];
using (AesGcm aesGcm = new AesGcm(aesGcmKey))
{
aesGcm.Encrypt(nonce, dataToEncrypt, cipherText, tag);
}
string encryptedCipher = Convert.ToBase64String(cipherText);
var keyString = Convert.ToBase64String(aesGcmKey);
var iVString = Convert.ToBase64String(nonce);
var tagString = Convert.ToBase64String(tag);
string aesGcmEncryptionString = string.Format("{0}:{1}:{2}", keyString, iVString, tagString);
#endregion
#region RSA Encryption
var rsa = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = rsa.ExportParameters(false);
RSAKeyInfo.Modulus = publicKey;
rsa.ImportParameters(RSAKeyInfo);
byte[] encryptedSymmetricKey = RSAEncrypt(Encoding.UTF8.GetBytes(aesGcmEncryptionString), RSAKeyInfo, false);
var enc_key = Convert.ToBase64String(encryptedSymmetricKey);
var requestBodyObject = new { message = encryptedCipher, enckey = enc_key };
string jsonRequestBody = JsonConvert.SerializeObject(requestBodyObject);
#endregion
#region Calculating Message Signature
Random random = new Random();
int ClientRequestId = random.Next(0, 99999999);
var time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var rawSignature = _ApiKey + ClientRequestId + time + jsonRequestBody;
HMACSHA256 hashObject = new HMACSHA256(Encoding.UTF8.GetBytes(_SecretKey));
var signature = hashObject.ComputeHash(Encoding.UTF8.GetBytes(rawSignature));
var computedHmac = Convert.ToBase64String(signature);
#endregion
#region Make Request
try
{
var client = new RestClient("< API URL>");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("contentType", "application/json");
request.AddHeader("clientRequestId", ClientRequestId.ToString());
request.AddHeader("apiKey", _ApiKey);
request.AddHeader("timestamp", time.Tostring);
request.AddHeader("messageSignature", computedHmac);
request.AddJsonBody(jsonRequestBody);
request.RequestFormat = DataFormat.Json;
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
#endregion
}
static string GetCertificate()
{
string cer = string.Empty;
// Code to get certicate .crt from the service.
//"<GET CERTICATE HERE>";
return cer;
}
static public byte[] RSAEncrpt(byte[] data, RSAParameters keyInfo, bool doOAEPPadding)
{
byte[] encryptedData;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(keyInfo);
encryptedData = rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1);
}
return encryptedData;
}
}
class Certificate
{
public string crt { get; set; }
}
How I can trace each steps as service is responding with incorrect message signature. Any way to verify and trace the above code steps?
Help!
Thanks in advance

Hybrid cryptosystem implementation in .net. Error Specified key is not a valid size for this algorithm

I am trying to implement hybrid cryptosystem as mentioned in https://en.wikipedia.org/wiki/Hybrid_cryptosystem
At the moment I have implemented following algorithm
private void button1_Click(object sender, EventArgs e)
{
CspParameters cspParams = new CspParameters { ProviderType = 1 };
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(1024, cspParams);
string publicKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(false));
string privateKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(true));
string symmericKey = "Kamran12";
txtEncryptedData.Text = EncryptData(txtInputData.Text, symmericKey);
string encryptedsymmetrickey = EncryptData(symmericKey, publicKey); //error line
//string decryptsymmetrickey = encryptedsymmetrickey + privateKey;
//string decrypteddata = encryptedData + decryptsymmetrickey;
}
public string EncryptData(string data, string key)
{
string encryptedData = null;
byte[] buffer = Encoding.UTF8.GetBytes(data);
DESCryptoServiceProvider desCryptSrvckey = new DESCryptoServiceProvider
{
Key = new UTF8Encoding().GetBytes(key)
};
desCryptSrvckey.IV = desCryptSrvckey.Key;
using (MemoryStream stmCipherText = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(stmCipherText, desCryptSrvckey.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(buffer, 0, buffer.Length);
cs.FlushFinalBlock();
encryptedData = Encoding.UTF8.GetString(stmCipherText.ToArray());
}
}
return encryptedData;
}
But getting error Specified key is not a valid size for this algorithm. at the time of encrypting the symmetric key
You are trying to encrypt using the (insecure) DES algorithm with an RSA public key. That's always going to fail, DESCryptoServiceProvider doesn't accept RSA keys. You'd need an RSACryptoServiceProvider for that.
You may want to consider using a specific library that already implements hybrid cryptography (PGP, CMS or one of the proprietary protocols). The way you are going at it your solution may run in the end, but it will not be secure.

.Net encryption and java decryption with RSA/ECB/PKCS1Padding

we have existing encryption code in java and which is working absolutely fine.I am trying to create same encryption method in .net which is failing java decryption method saying bad padding exception. See the code details below:
Working Java Code:
Encryption:
private static byte[] doThis(String message) {
byte[] messageCrypte = null;
try {
// Certificate Input Stream
// LA SSL Certificate to be passed.
InputStream inStream = new FileInputStream(certificate);
// X509Certificate created
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);
inStream.close();
// Getting Public key using Certficate
PublicKey rsaPublicKey = (PublicKey) cert.getPublicKey();
Cipher encryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "SunJCE");
encryptCipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey);
byte[] messageACrypter = message.getBytes();
// Encrypted String
messageCrypte = encryptCipher.doFinal(messageACrypter);
} catch (Exception e) {
// TODO: Exception Handling
e.printStackTrace();
}
return messageCrypte;
}
Equivalent c# .Net code I am trying to use but I am getting bad padding exception form java decryption code.
static byte[] doThis(string message)
{
X509Certificate cert = new X509Certificate(#"C:\Data\abc-rsa-public-key-certificate.cer");
byte[] aa = cert.GetPublicKey();
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
byte[] Exponent = { 1, 0, 1 };
RSAKeyInfo = RSA.ExportParameters(false);
//Set RSAKeyInfo to the public key values.
RSAKeyInfo.Modulus = aa;
//RSAKeyInfo.Exponent = Exponent;
RSA.ImportParameters(RSAKeyInfo);
byte[] bb = RSA.Encrypt(GetBytes(message), false);
return bb;
}
Java code for decryption
private String getDecryptedString(byte[] credentials, PrivateKey secretKey) throws NoSuchAlgorithmException,
NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
BadPaddingException {
String decryptedString;
Cipher decryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "SunJCE");
decryptCipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] messageDecrypte = decryptCipher.doFinal(credentials);
decryptedString = new String(messageDecrypte);
return decryptedString;
}
Here is the .net code:
public static string EncrypIt(string inputString, X509Certificate2 cert)
{
RSACryptoServiceProvider rsaservice = (RSACryptoServiceProvider)cert.PublicKey.Key;
byte[] plaintext = Encoding.UTF8.GetBytes(inputString);
byte[] ciphertext = rsaservice.Encrypt(plaintext, false);
string cipherresult = Convert.ToBase64String(ciphertext);
return cipherresult;
}

RSA .NET encryption Java decryption

I am trying to encrypt strings in .NET by using a RSA algorithm and decrypt the result in Java. At the moment, I have been able to do the opposite (Encrypt in Java, Decrypt in .NET).
Here I have my code that actually works (JAVA encryption):
byte[] modulusBytes = Base64.decode("2rRVVVFJRbH/wAPDtnwZwu+nxU+AZ6uXxh/sW+AMCBogg7vndZsnRiHoLttYYPqOyOhfgaBOQogrIfrKL4lipK4m52SBzw/FfcM9DsKs/rYR83tBLiIAfgdnVjF27tZID+HJMFTiI30mALjr7+tfp+2lIACXA1RIKTk7S9pDmX8=");
byte[] exponentBytes = Base64.decode("AQAB");
BigInteger modulus = new BigInteger(1, modulusBytes );
BigInteger exponent = new BigInteger(1, exponentBytes);
RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(rsaPubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] plainBytes = new String("big kitty dancing").getBytes("UTF-8");
byte[] cipherData = cipher.doFinal( plainBytes );
String encryptedString = Base64.encode(cipherData);
return encryptedString;
And (.NET decryption)
const int PROVIDER_RSA_FULL = 1;
const string CONTAINER_NAME = "Tracker";
CspParameters cspParams;
cspParams = new CspParameters(PROVIDER_RSA_FULL);
cspParams.KeyContainerName = CONTAINER_NAME;
RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(cspParams);
rsa1.FromXmlString("<RSAKeyValue><Modulus>2rRVVVFJRbH/wAPDtnwZwu+nxU+AZ6uXxh/sW+AMCBogg7vndZsnRiHoLttYYPqOyOhfgaBOQogrIfrKL4lipK4m52SBzw/FfcM9DsKs/rYR83tBLiIAfgdnVjF27tZID+HJMFTiI30mALjr7+tfp+2lIACXA1RIKTk7S9pDmX8=</Modulus><Exponent>AQAB</Exponent><P>+lXMCEwIN/7+eMpBrq87kQppxu3jJBTwztGTfXNaPUTx+A6uqRwug5oHBbSpYXKNDNCBzVm/0VxB3bo4FJx+ZQ==</P><Q>yasOGaJaE9xlF9T2xRuKeG9ZxCiyjhYaYB/mbtL+SIbtkRLi/AxaU4g2Il/UxhxhSXArKxIzV28zktispPJx1Q==</Q><DP>ueRgQIEFUV+fY979a1RgrVHIPpqEI1URhOMH3Q59oiXCcOumM5njyIHmWQxRAzXnG+7xlKXi1PrnRll0L4oOKQ==</DP><DQ>dfEMNgG1HJhwpxdtmqkYuoakwQvsIRzcIAuIAJh1DoWaupWJGk8/JEstHb1d+t7uJrzrAi2KyT/HscH2diE0YQ==</DQ><InverseQ>YoYF9PF6FiC0YngVeaC/eqt/ea8wMYNN3YO1LuzWpcy2exPRj2U0ZbWMvHXMUb4ea2qmhZGx1QlK4ULAuWKpXQ==</InverseQ><D>g1WAWI4pEK9TA7CA2Yyy/2FzzNiu0uQCuE2TZYRNiomo96KQXpxwqAzZLw+VDXfJMypwDMAVZe/SqzSJnFEtZxjdxaEo3VLcZ1mnbIL0vS7D6iFeYutF9kF231165qGd3k2tgymNMMpY7oYKjS11Y6JqWDU0WE5hjS2X35iG6mE=</D></RSAKeyValue>");
string data2Decrypt = "BaB21vY+RD/jiY3AAsb269fIWTEH38s0xLUfJ7CoVUgaQ6vYzB0tiJ1Ag9HNEdCcuZdGchhqnms8jpsqsHC1iKrz6QCLsgUU7VNWDfQqZYR6Rl/GwR0biK2STnOL+g06f/JUdixHOHOgROify1m8qppYo5plpOVMqYFzEMREMkM=";
byte[] encyrptedBytes = Convert.FromBase64String(data2Decrypt);
byte[] plain = rsa1.Decrypt(encyrptedBytes, false);
string decryptedString = System.Text.Encoding.UTF8.GetString(plain);
Console.WriteLine("SALIDA: " + decryptedString);
Now I want to do the opposite... But I get some errors like (the size of the key should be 128 bytes... etc) How should I do it?
Here I add the current non working code:
.NET
public string Encrypt(string text)
{
const int PROVIDER_RSA_FULL = 1;
const string CONTAINER_NAME = "Tracker";
CspParameters cspParams;
cspParams = new CspParameters(PROVIDER_RSA_FULL);
cspParams.KeyContainerName = CONTAINER_NAME;
RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(cspParams);
rsa1.FromXmlString("<RSAKeyValue><Modulus>2rRVVVFJRbH/wAPDtnwZwu+nxU+AZ6uXxh/sW+AMCBogg7vndZsnRiHoLttYYPqOyOhfgaBOQogrIfrKL4lipK4m52SBzw/FfcM9DsKs/rYR83tBLiIAfgdnVjF27tZID+HJMFTiI30mALjr7+tfp+2lIACXA1RIKTk7S9pDmX8=</Modulus><Exponent>AQAB</Exponent><P>92jJJyzFBSx6gL4Y1YpALmc5CNjoE/wETjqb3ci2v0+3rZWvJKmKy1ZEdlXpyuvXVksJ6cMdUpNAkMknUk9pTQ==</P><Q>4kxkABZOXyDLryYGCGY0b8N0FIdu5BTCFDYEdcatxl/f7ZGDS1NgHJpUWxkVXFfHy2Y/GuDOIbpcwlsO739H+w==</Q><DP>5bNFvrdUHF+VRN45VFjNCcgQLeSkY5mBrdfASoNFGA29LM5iE5nNIMfxPCS7sQiRnq6Af6YFHVtVgJchiMvtqQ==</DP><DQ>j+ng1qVY5epnXlWiFIla45C7K6sNfIMvAcdwgq39KWEjeWPGyYqWXtpOtzh2eylf6Bx4GVHKBW0NPJTIJMsfLQ==</DQ><InverseQ>8uu0dfPVDqB2qFM1Vdi8hl+2uZtN7gjT2co1cEWy29HVYBZD0k9KKCf2PbkeuSfpgFpE70wW5Hrp8V7l/SwSOw==</InverseQ><D>MM/c18zroJ2Iqi9s5/asvUBF3pjO3NSEbFjFpP/NT6WdKimvECWPz2xT6NlV0Vc6tQaAAmtn7Bt+HPhfVdrA4/ysYVe3/6TWkPjW+bvAhMWu/ZqISx11/jPYSGD9g3ZXgUiqcQM8UbOjlswoq4fpheEXTB0xdVutDLpO3qgHN6k=</D></RSAKeyValue>");
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
byte[] textBytes = encoding.GetBytes(text);
byte[] encryptedOutput = rsa1.Encrypt(textBytes, false);
string outputB64 = Convert.ToBase64String(encryptedOutput);
Console.WriteLine(outputB64);
return outputB64;
}
Java
public static String Decrypt(String encodedString) throws IllegalBlockSizeException, UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, BadPaddingException
{
byte[] modulusBytes = Base64.decode("2rRVVVFJRbH/wAPDtnwZwu+nxU+AZ6uXxh/sW+AMCBogg7vndZsnRiHoLttYYPqOyOhfgaBOQogrIfrKL4lipK4m52SBzw/FfcM9DsKs/rYR83tBLiIAfgdnVjF27tZID+HJMFTiI30mALjr7+tfp+2lIACXA1RIKTk7S9pDmX8=");
byte[] exponentBytes = Base64.decode("AQAB");
BigInteger modulus = new BigInteger(1, modulusBytes );
BigInteger exponent = new BigInteger(1, exponentBytes);
RSAPrivateKeySpec rsaPrivKey = new RSAPrivateKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey privKey = fact.generatePrivate(rsaPrivKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] base64String = Base64.decode(encodedString);
byte[] plainBytes = new String(base64String).getBytes("UTF-8");
byte[] cipherData = cipher.doFinal(plainBytes);
System.out.println(cipherData);
return cipherData.toString();
}
The last few lines of your Java decrypt code do not make sense. These lines are:
byte[] base64String = Base64.decode(encodedString);
byte[] plainBytes = new String(base64String).getBytes("UTF-8");
byte[] cipherData = cipher.doFinal(plainBytes);
System.out.println(cipherData);
return cipherData.toString();
You have to reverse the order of the steps you used to encrypt in .NET. First, you should Base64 decode the encoded string to get the cipher bytes. You did that, but you mislabeled the result as base64String. You probably should call this result cipherData. Second, you need to decrypt cipherData to get plain text. Third, you should create a string from plainbytes using the two-arg String constructor with the Charset for the second argument. Here is what the code should look like, or close to it.
byte[] cipherData = Base64.decode(encodedString);
byte[] plainBytes = cipher.doFinal(cipherData);
return new String(plainBytes, "UTF-8");
Finally, in Java every object has a toString() method but it doesn't always do what you want. For arrays the toString() method simply returns a representation of object id for that array, sort of the JVM equivalent of a memory address.
EDIT:
I missed that you are also using the wrong key in your decrypt code. Your are using the RSA public key, but you must instead use the RSA private key.
Here is the answer I couldn't post yesterday, related to the first answer to my post.
Well, I have tested the code and I have some problems. I have tried not to change anything unless it was completely necessary.
First I get an error here:
Cipher rsaCipher = Cipher.getInstance(RSA_ALGORITHM, CRYPTO_PROVIDER);
The "Entrust" crypto provider is not recognized... So I left just the first parameter. Then I get this error:
javax.crypto.BadPaddingException: Data must start with zero
I have tried through a WebService written in .NET which returns always byte arrays. Maybe there is some kind of problem in the translation. I know that I have to use Base64 numbers and (if I don't use AES) I have to break my Strings into pieces with the size of 128 bytes (limited by the RSA key).
I am still working on the problem to realize why I could encrypt in Java and decrypt in .NET but not the opposite.
Thanks again for your help!!
As you requested are hare some code snippets. RSA keys are from x509 certs.
Java RSA/AES:
// symmetric algorithm for data encryption
final String ALGORITHM = "AES";
// Padding for symmetric algorithm
final String PADDING_MODE = "/CBC/PKCS5Padding";
// character encoding
final String CHAR_ENCODING = "UTF-8";
// provider for the crypto
final String CRYPTO_PROVIDER = "Entrust";
// RSA algorithm used to encrypt symmetric key
final String RSA_ALGORITHM = "RSA/ECB/PKCS1Padding";
// symmetric key size (128, 192, 256) if using 192+ you must have the Java
// Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files
// installed
int AES_KEY_SIZE = 256;
private byte[] encryptWithRSA(byte[] aesKey, X509Certificate cert)
throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
// get the public key from the encryption certificate to encrypt with
PublicKey pubKey = cert.getPublicKey();
// get an instance of the RSA Cipher
Cipher rsaCipher = Cipher.getInstance(RSA_ALGORITHM);
// set the cipher to use the public key
rsaCipher.init(Cipher.ENCRYPT_MODE, pubKey);
// encrypt the aesKey
return rsaCipher.doFinal(aesKey);
}
private AESEncryptedContents encryptWithAes(byte[] dataToEncrypt)
throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException,
BadPaddingException, NoSuchProviderException {
// get the symmetric key generator
KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM);
keyGen.init(AES_KEY_SIZE); // set the key size
// generate the key
SecretKey skey = keyGen.generateKey();
// convert to binary
byte[] rawAesKey = skey.getEncoded();
// initialize the secret key with the appropriate algorithm
SecretKeySpec skeySpec = new SecretKeySpec(rawAesKey, ALGORITHM);
// get an instance of the symmetric cipher
Cipher aesCipher = Cipher.getInstance(ALGORITHM + PADDING_MODE,
CRYPTO_PROVIDER);
// set it to encrypt mode, with the generated key
aesCipher.init(Cipher.ENCRYPT_MODE, skeySpec);
// get the initialization vector being used (to be returned)
byte[] aesIV = aesCipher.getIV();
// encrypt the data
byte[] encryptedData = aesCipher.doFinal(dataToEncrypt);
// package the aes key, IV, and encrypted data and return them
return new AESEncryptedContents(rawAesKey, aesIV, encryptedData);
}
private byte[] decryptWithAES(byte[] aesKey, byte[] aesIV,
byte[] encryptedData) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException,
BadPaddingException, UnsupportedEncodingException,
NoSuchProviderException {
// initialize the secret key with the appropriate algorithm
SecretKeySpec skeySpec = new SecretKeySpec(aesKey, ALGORITHM);
// get an instance of the symmetric cipher
Cipher aesCipher = Cipher.getInstance(ALGORITHM + PADDING_MODE,
CRYPTO_PROVIDER);
// set it to decrypt mode with the AES key, and IV
aesCipher.init(Cipher.DECRYPT_MODE, skeySpec,
new IvParameterSpec(aesIV));
// decrypt and return the data
byte[] decryptedData = aesCipher.doFinal(encryptedData);
return decryptedData;
}
private byte[] decryptWithRSA(byte[] encryptedAesKey, PrivateKey privKey)
throws IllegalBlockSizeException, BadPaddingException,
InvalidKeyException, NoSuchAlgorithmException,
NoSuchPaddingException, NoSuchProviderException {
// get an instance of the RSA Cipher
Cipher rsaCipher = Cipher.getInstance(RSA_ALGORITHM, CRYPTO_PROVIDER);
// set the cipher to use the public key
rsaCipher.init(Cipher.DECRYPT_MODE, privKey);
// encrypt the aesKey
return rsaCipher.doFinal(encryptedAesKey);
}
C# .Net:
public byte[] encryptData(byte[] data, out byte[] encryptedAesKey, out byte[] aesIV) {
if (data == null)
throw new ArgumentNullException("data");
byte[] encryptedData; // data to return
// begin AES key generation
RijndaelManaged aesAlg = new RijndaelManaged();
aesAlg.KeySize = AES_KEY_SIZE;
aesAlg.GenerateKey();
aesAlg.GenerateIV();
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.PKCS7;
// aes Key to be encrypted
byte[] aesKey = aesAlg.Key;
// aes IV that is passed back by reference
aesIV = aesAlg.IV;
//get a new RSA crypto service provider to encrypt the AES key with the certificates public key
using (RSACryptoServiceProvider rsaCSP = new RSACryptoServiceProvider())
{
//add the certificates public key to the RSA crypto provider
rsaCSP.FromXmlString(encryptionCertificate.PublicKey.Key.ToXmlString(false));
//encrypt AES key with RSA Public key
//passed back by reference
encryptedAesKey = rsaCSP.Encrypt(aesKey, false);
//get an aes encryptor instance
ICryptoTransform aesEncryptor = aesAlg.CreateEncryptor();
encryptedData = encryptWithAes(aesEncryptor, data);
}
if (encryptedData == null)
throw new CryptographicException(
"Fatal error while encrypting with AES");
return encryptedData;
}
private byte[] encryptWithAes(ICryptoTransform aesEncryptor, byte[] data) {
MemoryStream memStream = null; // stream to write encrypted data to
CryptoStream cryptoStream = null; // crypto stream to encrypted data
try {
memStream = new MemoryStream();
// initiate crypto stream telling it to write the encrypted data to
// the memory stream
cryptoStream = new CryptoStream(memStream, aesEncryptor,
CryptoStreamMode.Write);
// write the data to the memory stream
cryptoStream.Write(data, 0, data.Length);
} catch (Exception ee) {
// rethrow
throw new Exception("Error while encrypting with AES: ", ee);
} finally {
// close 'em
if (cryptoStream != null)
cryptoStream.Close();
if (memStream != null)
memStream.Close();
}
// return the encrypted data
return memStream.ToArray();
}

CryptographicException intermittently occurs when encrypting/decrypting with RSA

I'm trying to encrypt and decrypt data using RSA in C#. I have the following MSTest unit test:
const string rawPassword = "mypass";
// Encrypt
string publicKey, privateKey;
string encryptedPassword = RSAUtils.Encrypt(rawPassword, out publicKey, out privateKey);
Assert.AreNotEqual(rawPassword, encryptedPassword,
"Raw password and encrypted password should not be equal");
// Decrypt
string decryptedPassword = RSAUtils.Decrypt(encryptedPassword, privateKey);
Assert.AreEqual(rawPassword, decryptedPassword,
"Did not get expected decrypted password");
It fails during decryption, but only sometimes. It seems like whenever I set breakpoints and step through the test, it passes. This made me think perhaps something wasn't finishing in time for decryption to occur successfully, and me slowing stepping through it while debugging gave it enough time to complete. When it fails, the line it seems to fail at is decryptedBytes = rsa.Decrypt(bytesToDecrypt, false); in the following method:
public static string Decrypt(string textToDecrypt, string privateKeyXml)
{
if (string.IsNullOrEmpty(textToDecrypt))
{
throw new ArgumentException(
"Cannot decrypt null or blank string"
);
}
if (string.IsNullOrEmpty(privateKeyXml))
{
throw new ArgumentException("Invalid private key XML given");
}
byte[] bytesToDecrypt = ByteConverter.GetBytes(textToDecrypt);
byte[] decryptedBytes;
using (var rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(privateKeyXml);
decryptedBytes = rsa.Decrypt(bytesToDecrypt, false); // fail here
}
return ByteConverter.GetString(decryptedBytes);
}
It fails with this exception:
System.Security.Cryptography.CryptographicException: Bad Data
My Encrypt method is as follows:
public static string Encrypt(string textToEncrypt, out string publicKey,
out string privateKey)
{
byte[] bytesToEncrypt = ByteConverter.GetBytes(textToEncrypt);
byte[] encryptedBytes;
using (var rsa = new RSACryptoServiceProvider())
{
encryptedBytes = rsa.Encrypt(bytesToEncrypt, false);
publicKey = rsa.ToXmlString(false);
privateKey = rsa.ToXmlString(true);
}
return ByteConverter.GetString(encryptedBytes);
}
The ByteConverter used throughout is just the following:
public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding();
I've seen a few questions on StackOverflow about RSA encryption and decryption with .NET. This one was due to encrypting with the private key and trying to decrypt with the public key, but I don't think I'm doing that. This question has the same exception as me, but the selected answer was to use OpenSSL.NET, which I would prefer not to do.
What am I doing wrong?
Could you replace ByteConverter.GetBytes with Convert.FromBase64String and replace ByteConverter.GetString with Convert.ToBase64String and see if that helps. Bad Data exception usually means that you have an invalid character in the data or that the length is not the correct length for decrypting. I think using the Convert functions might fix your problems.
public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding();
public static string Encrypt(string textToEncrypt, out string publicKey,
out string privateKey)
{
byte[] bytesToEncrypt = ByteConverter.GetBytes(textToEncrypt);
byte[] encryptedBytes;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
encryptedBytes = rsa.Encrypt(bytesToEncrypt, false);
publicKey = rsa.ToXmlString(false);
privateKey = rsa.ToXmlString(true);
}
return Convert.ToBase64String(encryptedBytes);
}
public static string Decrypt(string textToDecrypt, string privateKeyXml)
{
if (string.IsNullOrEmpty(textToDecrypt))
{
throw new ArgumentException(
"Cannot decrypt null or blank string"
);
}
if (string.IsNullOrEmpty(privateKeyXml))
{
throw new ArgumentException("Invalid private key XML given");
}
byte[] bytesToDecrypt = Convert.FromBase64String(textToDecrypt);
byte[] decryptedBytes;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(privateKeyXml);
decryptedBytes = rsa.Decrypt(bytesToDecrypt, false); // fail here
}
return ByteConverter.GetString(decryptedBytes);
}
Your problem is with the conversion from bytes to string. Not all sequences of bytes are a valid UTF-16 encoding and you are using a UnicodeEncoding that silently ignores invalid bytes. If you used
public static readonly UnicodeEncoding ByteConverter = new UnicodeEncoding(false, false, true);
instead, your code would have failed when trying to convert the bytes instead of silently replacing the invalid byte-pairs with 0xFFFD.
The fact that the test worked while debugging was a coincidence. You are using a random RSA key-pair, so sometimes you will get a encryption that is a valid UTF-16 encoding.
The fix is, as SwDevMan81 suggests, to use an encoding that can convert all possible byte-arrays. F.x. Base64-encoding.
I would recommend using this class, sadly I don't remember the original author though..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
namespace Encryption
{
class AsymmetricED
{
private static RSAParameters param = new RSAParameters();
/// <summary>
/// Get Parameters
/// </summary>
/// <param name="pp">Export private parameters?</param>
/// <returns></returns>
public static RSAParameters GenerateKeys(bool pp)
{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
if (param.Equals(new RSAParameters()))
{
param = RSA.ExportParameters(true);
}
RSA.ImportParameters(param);
return RSA.ExportParameters(pp);
}
static public byte[] RSAEncrypt(byte[] DataToEncrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
{
try
{
//Create a new instance of RSACryptoServiceProvider.
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
//Import the RSA Key information. This only needs
//toinclude the public key information.
RSA.ImportParameters(RSAKeyInfo);
//Encrypt the passed byte array and specify OAEP padding.
//OAEP padding is only available on Microsoft Windows XP or
//later.
return RSA.Encrypt(DataToEncrypt, DoOAEPPadding);
}
//Catch and display a CryptographicException
//to the console.
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
}
static public byte[] RSADecrypt(byte[] DataToDecrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
{
try
{
//Create a new instance of RSACryptoServiceProvider.
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
//Import the RSA Key information. This needs
//to include the private key information.
RSA.ImportParameters(RSAKeyInfo);
//Decrypt the passed byte array and specify OAEP padding.
//OAEP padding is only available on Microsoft Windows XP or
//later.
return RSA.Decrypt(DataToDecrypt, DoOAEPPadding);
}
//Catch and display a CryptographicException
//to the console.
catch (CryptographicException e)
{
ConsoleColor col = Console.BackgroundColor;
Console.BackgroundColor = ConsoleColor.Red;
Console.WriteLine(e.ToString());
Console.BackgroundColor = col;
return null;
}
}
}
}
Use as:
Encryption.AsymmetricED.RSAEncrypt(Data, GenerateKeys(false), false);
Encryption.AsymmetricED.RSADecrypt(Data, GenerateKeys(true), false);
EDIT:
I also recommend that you don't use this for large data encryption. Usually you would encrypt the actual data with a symmetric algorithm (AES, etc), then encrypt the symmetric key (randomly generated) with the RSA algorithm, then send the rsa encrypted symmetric key, and the symmetric key data..
You should also look at RSA signing, to make sure the data is coming from where it says it is..

Categories