AES Encryption in android and Decryption - c#

I am trying to encrypt and decrypt my messages from my android app and been successful with the below code.
I want to decrypt this message in my c# based web application service. Can anyone please help me out with the equivalent code in c# for the below encryption code**.
this is the encrypting code
// encrypt the message
byte[] encryptedMsg = encryptSMS(secretKeyString,
msgContentString);
// convert the byte array to hex format in order for
// transmission
String msgString = byte2hex(encryptedMsg);
// send the message through SMS
sendSMS(recNumString, msgString);
// finish
finish();
} else
Toast.makeText(getBaseContext(),"Please enter phone number, secret key and the message. Secret key must be 16 characters!",Toast.LENGTH_SHORT).show();
}
});
}
public static void sendSMS(String recNumString, String encryptedMsg) {
try {
// get a SmsManager
SmsManager smsManager = SmsManager.getDefault();
// Message may exceed 160 characters
// need to divide the message into multiples
ArrayList<String> parts = smsManager.divideMessage(encryptedMsg);
smsManager.sendMultipartTextMessage(recNumString, null, parts,
null, null);
} catch (Exception e) {
e.printStackTrace();
}
}
// utility function
public static String byte2hex(byte[] b) {
String hs = "";
String stmp = "";
for (int n = 0; n < b.length; n++) {
stmp = Integer.toHexString(b[n] & 0xFF);
if (stmp.length() == 1)hs += ("0" + stmp);
else hs += stmp;
}
return hs.toUpperCase();
}
// encryption function
public static byte[] encryptSMS(String secretKeyString,
String msgContentString) {
try {
byte[] returnArray;
// generate AES secret key from user input
Key key = generateKey(secretKeyString);
// specify the cipher algorithm using AES
Cipher c = Cipher.getInstance("AES");
// specify the encryption mode
c.init(Cipher.ENCRYPT_MODE, key);
// encrypt
returnArray = c.doFinal(msgContentString.getBytes());
return returnArray;
} catch (Exception e) {
e.printStackTrace();
byte[] returnArray = null;
return returnArray;
}
}
private static Key generateKey(String secretKeyString) throws Exception {
Key key = new SecretKeySpec(secretKeyString.getBytes(), "AES");
return key;
}
}
and this is the decrypting code
// utility function: convert hex array to byte array
public static byte[] hex2byte(byte[] b) {
if ((b.length % 2) != 0) throw new IllegalArgumentException("hello");
byte[] b2 = new byte[b.length / 2];
for (int n = 0; n < b.length; n += 2) {
String item = new String(b, n, 2);
b2[n / 2] = (byte) Integer.parseInt(item, 16);
}
return b2;
}
// decryption function
public static byte[] decryptSMS(String secretKeyString, byte[] encryptedMsg)
throws Exception {
// generate AES secret key from the user input secret key
Key key = generateKey(secretKeyString);
// get the cipher algorithm for AES
Cipher c = Cipher.getInstance("AES");
// specify the decryption mode
c.init(Cipher.DECRYPT_MODE, key);
// decrypt the message
byte[] decValue = c.doFinal(encryptedMsg);
return decValue;
}
private static Key generateKey(String secretKeyString) throws Exception {
// generate AES secret key from a String
Key key = new SecretKeySpec(secretKeyString.getBytes(), "AES");
return key;
}
}

Related

Getting bad padding exception while decrypting using AES CBC PCKS7 padding

So I have some code in Java for encrypting and decrypting objects and I've been trying to do the same in C# so my server and client can decrypt and encrypt messages. At the moment the last piece of the puzzle I need to fix is my C# decryption. When attempting to decrypt, I usually get a bad padding exception of sorts. However, Java is using PCKS5 padding and C# is using PCKS7 padding which if I understand correctly are virtually indistinguishable.
#RequiredArgsConstructor
#Getter
public class EncryptedBytes {
private final byte[] data;
private final byte[] params;
private final String paramAlgorithm;
}
/**
* Encrypt object with password
*
* #param data Object to be encrypted
* #param secret Password to use for encryption
* #return Encrypted version of object
*/
public static EncryptedBytes encrypt(String data, SecretKey secret) throws InvalidKeyException {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
// properly encode the complete ciphertext
//logEncrypt(password, object);
byte[] encodedData = cipher.doFinal(data.getBytes(CharsetUtil.UTF_8));
byte[] params = cipher.getParameters().getEncoded();
String paramAlgorithm = cipher.getParameters().getAlgorithm();
return new EncryptedBytes(encodedData, params, paramAlgorithm);
} catch (InvalidKeyException e) {
throw e;
} catch (NoSuchAlgorithmException | IllegalBlockSizeException | NoSuchPaddingException | BadPaddingException | IOException e) {
e.printStackTrace();
}
return null;
}
/**
* Decrypt data with secret
*
* #param encryptedBytes Object to be decrypted
* #param secret Password to use for decryption
* #return Decrypted version of object
*/
public static String decrypt(EncryptedBytes encryptedBytes, #NonNull SecretKey secret) throws InvalidKeyException {
try {
// get parameter object for password-based encryption
AlgorithmParameters algParams = AlgorithmParameters.getInstance(encryptedBytes.getParamAlgorithm()); // getParamAlgorithm usually just returns "AES"
if (algParams == null) throw new IllegalArgumentException("EncryptedBytes.Parameters are not valid");
// initialize with parameter encoding from above
algParams.init(encryptedBytes.getParams());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, algParams);
return new String(cipher.doFinal(encryptedBytes.getData()), CharsetUtil.UTF_8);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | IOException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return null;
}
Now in C# I have a very close implementation to the Java code above.
public class EncryptedBytes
{
[NotNull]
public List<sbyte> data { get; }
[NotNull]
[JsonProperty("params", Required = Required.Always)]
[JsonPropertyName("params")]
public List<sbyte> keyParams { get; }
[NotNull]
public string paramAlgorithm { get; }
public EncryptedBytes(IEnumerable<sbyte> data, [JsonProperty(propertyName: "params")] IEnumerable<sbyte> keyParams, string paramAlgorithm)
{
this.data = data.ToList();
this.keyParams = keyParams.ToList();
this.paramAlgorithm = paramAlgorithm;
}
}
public static EncryptedBytes encrypt(AesCryptoServiceProvider aesCryptoServiceProvider, string plainText,
Encoding encoding)
{
if (encoding == null) encoding = StaticHandler.encoding; // UTF_8
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (aesCryptoServiceProvider.Key == null || aesCryptoServiceProvider.Key.Length <= 0)
throw new ArgumentNullException("Key");
if (aesCryptoServiceProvider.IV == null || aesCryptoServiceProvider.IV.Length <= 0)
throw new ArgumentNullException("IV");
ICryptoTransform transform = aesCryptoServiceProvider.CreateEncryptor();
var encodedText = encoding.GetBytes(plainText);
var encryptedText =
transform.TransformFinalBlock(encodedText, 0, encodedText.Length).Select(Convert.ToSByte);
var derOctetString = new DerOctetString(aesCryptoServiceProvider.IV);
return new EncryptedBytes(encryptedText, derOctetString.GetEncoded().Select(Convert.ToSByte), "AES");
}
public static string decrypt([NotNull] EncryptedBytes encryptedBytes, AesCryptoServiceProvider aesCryptoServiceProvider, Encoding? encoding)
{
if (encoding == null) encoding = StaticHandler.encoding;
var cipherText = encryptedBytes.data.Select(arg => (byte) arg).ToArray();
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (aesCryptoServiceProvider.Key == null || aesCryptoServiceProvider.Key.Length <= 0)
throw new ArgumentNullException("Key");
if (aesCryptoServiceProvider.IV == null || aesCryptoServiceProvider.IV.Length <= 0)
throw new ArgumentNullException("IV");
// Declare the string used to hold
// the decrypted text.
aesCryptoServiceProvider.Padding = PaddingMode.PKCS7;
var transform = aesCryptoServiceProvider.CreateDecryptor();
string plaintext;
var keyParams = encryptedBytes.keyParams.Select(b => (byte) b).ToArray();
var octetString = (DerOctetString) Asn1Object.FromByteArray(keyParams);
aesCryptoServiceProvider.IV = octetString.GetOctets();
aesCryptoServiceProvider.Padding = PaddingMode.PKCS7;
plaintext = encoding.GetString(transform.TransformFinalBlock(cipherText, 0, cipherText.Length));
return plaintext;
}
The main code in question is this:
var transform = aesCryptoServiceProvider.CreateDecryptor();
string plaintext;
var keyParams = encryptedBytes.keyParams.Select(b => (byte) b).ToArray();
var octetString = (DerOctetString) Asn1Object.FromByteArray(keyParams);
aesCryptoServiceProvider.IV = octetString.GetOctets();
aesCryptoServiceProvider.Padding = PaddingMode.PKCS7;
plaintext = encoding.GetString(transform.TransformFinalBlock(cipherText, 0, cipherText.Length));
For testing purposes, you may use this value as an example of data that is parsed (EncryptedBytes class converted to JSON)
{"data":[-91,-105,-48,-75,18,-64,-12,-122,56,76,17,101,54,65,53,-10],"params":[4,16,81,-32,-95,-80,-81,100,-81,58,49,-36,35,21,-115,-110,111,-2],"paramAlgorithm":"AES"}
This should just return empty brackets as plaintext.
Expected result:
{}
You may use this key: (both values are the same key, written differently)
Byte array: 15,215,133,73,75,92,34,113,122,135,36,209,240,159,54,165,30,23,86,73,166,7,166,192,209,145,68,167,19,205,105,55
Base64: D9eFSUtcInF6hyTR8J82pR4XVkmmB6bA0ZFEpxPNaTc=

AES encryption in C Sharp and decryption in Android using PBKDF2WithHmacSHA1

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.

Java RSA/ECB/PKCS1Padding encryption with .NET

I need to encrypt a password on a web site using a public key. The public key is delivered through a Java web service with the information: The key is a RSA key to be used with RSA/ECB/PKCS1Padding algorithm. The public key is delivered as JSON in the form:
{
"kid":"PWD",
"kty":"RSA",
"use":"enc",
"n":"MTA0OTgzNjg0OTMxMzE2NjkwNTU4Mjg3NDIwMDg1NTY0ODEyMjg1MDk2NTcwNzU5NDIzNzM0O
DA3OTA2MzA0MDczNTU0NDQ2Njg3ODY2ODk2NTk0NjYzNTAxMzg0NzE1OTExMjA0MjU1MzMzOTIzMjA
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
zcwMjA3MzQxOTcwNzc4NDAwNzM3MTY2NDMyNzIwNjMwMDQwOTMwOTQ0MTA2OTE1NDEzMDAwNTMyMTE
5ODM0MTA2MjAzMDIyODEwMjYyMDM3MDQ0NzkxNzIzNTU1MjQyNjYxMzE2ODc4OTc5NzY1OTgzMjg4M
zQ0NDc3OTYwNTg3MzE2NTUwMDgx",
"e":"NjU1Mzc"
}
I tried to encrypt the password using the public key but the key generated is not correct. Here is my code:
encryptedPassword = EncrypIt("Password01", "MTA1MzQxNzIwODA3NjUwNzg5ND
Y4ODU2Mjc0NDA3ODIwMjQ1ODQ5NDE1MDk1MDIzMTM3MjM0NzAwNzYzNjc2MTgwNjg3ODMxMjA3
NTY0NTcxMjg2MzM4NjQ1NzEwMDcyMjY2MTQyNDIzMTczMjkwMDk0MTc0MTA5MTc5MzI0NjYwMjQ4NzI3NzM0MTQ5NDY0MjUwODkwO
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
c2OTAzNjc3NzQzODM3NzM0MjE2ODM0NjY4MjM4MTQ0OTA3MDQ3MTk1Njc1NzU3OTE2NjEyNzkzMzM2MzI3MDUyNjg0NDI5NDExNjI
2MzA0MzM5", "NjU1Mzc");
public static string EncrypIt(string password, string publicKey, string exponent)
{
UnicodeEncoding ByteConverter = new UnicodeEncoding();
byte[] publicKeyByte = ByteConverter.GetBytes(publicKey);
byte[] passwordByte = ByteConverter.GetBytes(password);
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo = RSA.ExportParameters(false); //Export only public key
//Set RSAKeyInfo to the public key values.
RSAKeyInfo.Modulus = publicKeyByte;
//RSAKeyInfo.Exponent = exponentBytes; //I tried to set exponent but I have an error
RSA.ImportParameters(RSAKeyInfo);
byte[] encryptedPassword = RSA.Encrypt(passwordByte, false);
return Convert.ToBase64String(encryptedPassword);
}
(The public key is different between the JSON and my code but don't pay attention to it, I just copied information from different sources)
The encrypted password I obtained is far too long: The encrypted password should be 172 characters (I know because I have a small Java program that allows me to encrypt passwords correctly) but I get 1100 characters.
I do not use the exponent: Should I ?
Could I not use the JSON to directly configure the RSACryptoServiceProvider correctly ?
The answer from owlstead helped me to get an encrypted password with the right string size but the service using the encrypted string reject it with the message: javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes
I otained the code of the java program wich is doing the correct encryption (see below). I need to achieve the same encryption using .NET.
public class EncryptionServiceImpl
{
private static final Charset UTF8 = Charset.forName("UTF-8");
#Resource(name = "briqueAuthentificationClient")
private BriqueAuthentificationClientImpl briqueAuthentificationClient;
protected static final String ALGORITHM_RSA = "RSA";
protected static final String TRANSFORMATION_RSA_ECB_PKCS1PADDING = "RSA/ECB/PKCS1Padding";
private static final Logger LOG = LoggerFactory.getLogger(EncryptionServiceImpl.class);
public EncryptionServiceImpl() {
LOG.info("constructeur EncryptionServiceImpl");
}
/**
* #param briqueAuthentificationClient the briqueAuthentificationClient to set
*/
public void setBriqueAuthentificationClient(final BriqueAuthentificationClientImpl briqueAuthentificationClient) {
this.briqueAuthentificationClient = briqueAuthentificationClient;
}
public String encrypt(final String input) throws GeneralSecurityException {
if (StringUtils.isNotBlank(input)) {
final CertificateDto certificate = this.briqueAuthentificationClient.getCurrentCertificate();
if (certificate != null) {
return new String(this.encryptAndEncode(input.getBytes(), certificate), EncryptionServiceImpl.UTF8);
} else {
throw new RuntimeException("Certificate is null");
}
}
return null;
}
protected byte[] encryptAndEncode(final byte[] input, final CertificateDto currentCertificate)
throws GeneralSecurityException {
// Création de la clé publique
final PublicKey publicKey = this.buildPublicKey(currentCertificate);
// Chiffre
final byte[] inputEncrypted = this.encrypte(input, publicKey);
// Encode
return this.encodeBase64Url(inputEncrypted);
}
protected PublicKey buildPublicKey(final CertificateDto currentCertificate) throws GeneralSecurityException {
if ("RSA".equals(currentCertificate.getKeyType())) {
return this.buildRSAPublicKey(currentCertificate);
}
LOG.error(String.format("Tentative de création d'une clé publique avec un algorithme non connu [%s]",
currentCertificate.getKeyType()));
return null;
}
protected PublicKey buildRSAPublicKey(final CertificateDto currentCertificate) throws GeneralSecurityException {
final BigInteger modulus = new BigInteger(new String(Base64.decodeBase64(currentCertificate.getModulus()),
EncryptionServiceImpl.UTF8));
final BigInteger publicExponent = new BigInteger(new String(Base64.decodeBase64(currentCertificate
.getPublicExponent()), EncryptionServiceImpl.UTF8));
try {
return KeyFactory.getInstance(ALGORITHM_RSA).generatePublic(new RSAPublicKeySpec(modulus, publicExponent));
} catch (InvalidKeySpecException e) {
throw e;
} catch (NoSuchAlgorithmException e) {
throw e;
}
}
protected byte[] encrypte(final byte[] input, final RSAPublicKeySpec rsaPublicKeySpec)
throws GeneralSecurityException {
PublicKey publicKey;
try {
publicKey = KeyFactory.getInstance(ALGORITHM_RSA).generatePublic(
new RSAPublicKeySpec(rsaPublicKeySpec.getModulus(), rsaPublicKeySpec.getPublicExponent()));
} catch (InvalidKeySpecException e) {
throw e;
} catch (NoSuchAlgorithmException e) {
throw e;
}
return this.encrypte(input, publicKey);
}
protected byte[] encrypte(final byte[] input, final PublicKey publicKey) throws GeneralSecurityException {
try {
final Cipher cipher = Cipher.getInstance(TRANSFORMATION_RSA_ECB_PKCS1PADDING);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(input);
} catch (NoSuchAlgorithmException e) {
throw e;
} catch (NoSuchPaddingException e) {
throw e;
} catch (IllegalBlockSizeException e) {
throw e;
} catch (BadPaddingException e) {
throw e;
}
}
protected byte[] decrypte(final byte[] input, final RSAPrivateKeySpec rsaPrivateKeySpec)
throws GeneralSecurityException {
final BigInteger modulus = rsaPrivateKeySpec.getModulus();
final BigInteger privateExponent = rsaPrivateKeySpec.getPrivateExponent();
try {
final PrivateKey privateKey = KeyFactory.getInstance(ALGORITHM_RSA).generatePrivate(
new RSAPrivateKeySpec(modulus, privateExponent));
final Cipher cipher = Cipher.getInstance(TRANSFORMATION_RSA_ECB_PKCS1PADDING);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(input);
} catch (NoSuchAlgorithmException e) {
throw e;
} catch (NoSuchPaddingException e) {
throw e;
} catch (IllegalBlockSizeException e) {
throw e;
} catch (BadPaddingException e) {
throw e;
} catch (InvalidKeySpecException e) {
throw e;
} catch (InvalidKeyException e) {
throw e;
}
}
protected byte[] encodeBase64Url(final byte[] input) {
return Base64.encodeBase64(input, false);
}
protected byte[] decodeBase64Url(final byte[] input) {
return Base64.decodeBase64(input);
}
/**
* Method to connect to an url
*
* #param httpclient the http connection
* #return the response GetMethod
* #throws OAuthException in cas of connection error
*/
private GetMethod connect(final HttpClient httpclient, final String url) {
final GetMethod httpget = new GetMethod(url);
try {
httpclient.executeMethod(httpget);
} catch (final UnknownHostException e) {
throw new RuntimeException("Connection ERROR - Host could not be determined.", e);
} catch (final IOException e) {
throw new RuntimeException("Connection ERROR - Input/Output error.", e);
}
return httpget;
}
}
The steps I accomplished with the help of owlstead are in the answer below.
When I use this Java program to encode the string Password01 I obtain a string like:
sy5/XElHvuYA4Rbq8OBydLymT82R+z77jy6MU2sNal7VenzPEbARgy7p3zWgYgG13Cypk+zbnnB5L37fVUhgOgCqCyLtikzxJBNmPyzUK9+beiHJKyONZwifDzQ44hXTeXcZ0bmF9b5dLFy9QS/N5m28vIyBSGY8K2B7EB2FF38=
This encrypted password can be decrypted on Java side
When I use the .NET code the string is like:
ACZXYj/KudyxKBB510SxSouKaVwssmEUM6Jpreh8jTtrIH9eGb18GIdkBU7rXzMuLYbAhyREbFLbR87zW/2DNa4tN97FOlqr6k1JppJ/SSS/9fGdMvSOAQbWjsxksDH7fRu9dCvK0m0exFtGfXG7Yua9bB1m0dTNiwCZUZM0LnEM
This encrypted password failed to be decrypted on Java side. Failed with the error message:
javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes
You first need to perform base 64 decoding using Convert.FromBase64String of n and e, convert the result from ASCII encoding to a string and then parse the result using BigInteger.parse. Then you can convert using toByteArray to bytes, but beware that BigInteger is little endian, and RSAParameters expects big endian, so you have to reverse the bytes.
Your .NET RSA ciphertext (which you probably shouldn't reverse) is preceded by a 00h valued byte, which makes it an invalid ciphertext. RSA ciphertext must be of the exact length in bytes of the key.
Thank you sirs for the answers and comments, it help me to finally solve my issue:
For exponent, I got an error message: Invalid length for a Base-64 char array or string
This is because a base 64 value should be a multiple of 4. If this is not the case we should append equal sign (=) to reach a multiple of 4.
Thus after changing the exponent string from "NjU1Mzc" to "NjU1Mzc=" the value can be decoded.
Then I applied the solution provided by owlstead. Here is the final code which works fine:
//Decode from base 64
byte[] publicKeyByte = Convert.FromBase64String(rsaPublicKey.modulo);
byte[] exponentByte = Convert.FromBase64String(rsaPublicKey.exponent);
//Convert to ASCII string
UTF8Encoding ByteConverter = new UTF8Encoding();
string publicKeyString = System.Text.Encoding.Default.GetString(publicKeyByte);
string exponentString = System.Text.Encoding.Default.GetString(exponentByte);
//Convert to BigInteger
BigInteger publicKeyBI = BigInteger.Parse(publicKeyString);
BigInteger exponentBI = BigInteger.Parse(exponentString);
//Convert back to byte array
byte[] publicKeyByte2 = publicKeyBI.ToByteArray();
byte[] exponentByte2 = exponentBI.ToByteArray();
//We must remove the 129th sign byte which is added when converting to BigInteger
if (publicKeyByte2.Length == 129) Array.Resize(ref publicKeyByte2, 128);
//Create crypto service
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
//Assign RSA key modulus/exponent reversing from little endian to big endian
RSAKeyInfo.Modulus = publicKeyByte2.Reverse().ToArray();
RSAKeyInfo.Exponent = exponentByte2.Reverse().ToArray();
RSA.ImportParameters(RSAKeyInfo);
//Convert password string to byte array
byte[] passwordByte = ByteConverter.GetBytes(clearPassword);
//Encrypt the password and encode 64
encryptedPassword = Convert.ToBase64String(RSA.Encrypt(passwordByte, false));
The missing point from owlstead is that: the method returns a byte array with an extra element whose value is zero to disambiguate positive values
See Microsoft documentation for more information on this point: BigInteger.ToByteArray Method
This code encrypts the password as a 172 characters string which ends with a = sign which is what I expected and it is correctly decrypted on Java side with the private key.
I tried this and it encrypts correctly
var input = "String to Encode.";
var mod = "MTA1MzQxNzIwODA3NjUwNzg5NDY4ODU2Mjc0NDA3ODIwMjQ1ODQ5NDE1MDk1MDIzMTM3MjM0NzAwNzYzNjc2MTgwNjg3ODMxMjA3NTY0NTcxMjg2MzM4NjQ1NzEwMDcyMjY2MTQyNDIzMTczMjkwMDk0MTc0MTA5MTc5MzI0NjYwMjQ4NzI3NzM0MTQ5NDY0MjUwODkwOXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXc2OTAzNjc3NzQzODM3NzM0MjE2ODM0NjY4MjM4MTQ0OTA3MDQ3MTk1Njc1NzU3OTE2NjEyNzkzMzM2MzI3MDUyNjg0NDI5NDExNjI2MzA0MzM5==";
var exp = "NjU1Mzc=";
var intValue = int.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(exp)));
var rsaParam = new RSAParameters();
rsaParam.Modulus = Convert.FromBase64String(mod);
rsaParam.Exponent = BitConverter.GetBytes(intValue);
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(rsaParam);
Console.WriteLine(Convert.ToBase64String(rsa.Encrypt(Encoding.UTF8.GetBytes(input), false)));
}
Console.ReadLine();
I think the issue is the exponent is strange. Exp = 65537;

Bad data exception when decrypting using RSA with correct private and public key pair in C#

This is my code for decryption process:
private RSACryptoServiceProvider _rsa;
private string _privateKey;
private string _publicKey;
public RsaLibrary()
{
//initialsing the RSA object taking the option of a 1024 key size
_rsa = new RSACryptoServiceProvider(1024);
_privateKey = _rsa.ToXmlString(true);
_publicKey = _rsa.ToXmlString(false);
}
public string Decrypt(string ciphertext, string privateKey_ = null)
{
if (String.IsNullOrEmpty(privateKey_))
{
return DecryptToBytes(ciphertext, _privateKey);
}
else
{
return DecryptToBytes(ciphertext, privateKey_);
}
}
private string DecryptToBytes(string ciphertext, string privateKey)
{
if (String.IsNullOrEmpty(privateKey))
{
throw new ArgumentNullException("Error: No key provided.");
}
if (ciphertext.Length<=0)
{
throw new ArgumentNullException("Error: No message to decrypt.");
}
byte[] plaintext;
byte[] ciphertext_Bytes = Encoding.Unicode.GetBytes(ciphertext);
_rsa.FromXmlString(privateKey);
plaintext = _rsa.Decrypt(ciphertext_Bytes, false);
return Encoding.Unicode.GetString(plaintext);
}
The encryption code:
private string EncryptToByte(string plaintext, string publicKey)
{
if (String.IsNullOrEmpty(publicKey))
{
throw new ArgumentNullException("Error: No key provided.");
}
if (plaintext.Length<=0)
{
throw new ArgumentNullException("Error: No message to incrypt");
}
byte[] ciphertext;
byte[] plaintext_Bytes = Encoding.Unicode.GetBytes(plaintext);
_rsa.FromXmlString(publicKey);
ciphertext = _rsa.Encrypt(plaintext_Bytes, false);
return Convert.ToBase64String(ciphertext);
}
I can not see where I am going wrong. I have made sure that the keys are correct. The public one which i extracted using this line in the constructor:
_publicKey = _rsa.ToXmlString(false);
This public key is displayed on the form that I created. The private i used the "true" instead of false.
Any ideas?
Ciphertext is very unlikely to be genuinely UTF-16-encoded text. Assuming that the encryption side had something like:
string encryptedText = Encoding.Unicode.GetString(encryptedBytes);
you've basically lost data. The result of encryption is not text - it's arbitrary binary data. If you want to convert that to text for some transport reason, you should use Base64, e.g.
string base64EncryptedText = Convert.ToBase64String(encryptedBytes);
Then use Convert.FromBase64String to recover the original encrypted binary data which is ready to decrypt.

RijndaelEncryption with Java then Decryption with C# and EnterpriseLibrary 4.1

I believe when the EnterpriseLibrary tries to decrypt a RijndaelManaged encrypted string it expects the Initialization Vector to be prepended to the encrypted text. Currently with the code below. I can decrypt the message with out an exception, but I am getting weird characters like:
�猀漀椀搀㴀眀最爀甀戀攀㄀☀甀琀挀㴀㈀ ㄀ ⴀ㄀ ⴀ㈀㄀吀㄀㌀㨀㔀㈀㨀㄀㌀
What do I need to do to make this work? Any help is greatly appreciated. Here is some of the code I have...
I have a C# application that decrypts data using the EnterpriseLibrary 4.1 (encryption: RijndaelManaged).
string message = "This encrypted message comes from Java Client";
Cryptographer.DecryptSymmetric("RijndaelManaged", message);
The client encryptes the message, implemented in Java.
public String encrypt(String auth) {
try {
String cipherKey = "Key as a HEX string";
byte[] rawKey = hexToBytes(cipherKey);
SecretKeySpec keySpec = new SecretKeySpec(rawKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
String cipherIV = "xYzF5AqA2cKLbvbfGzsMwg==";
byte[] btCipherIV = Base64.decodeBase64(cipherIV.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec (btCipherIV));
byte[] unencrypted = StringUtils.getBytesUtf16(auth);
byte[] encryptedData = cipher.doFinal(unencrypted);
String encryptedText = null;
byte[] entlib = new byte[btCipherIV2.length + encryptedData.length];
System.arraycopy(btCipherIV, 0, entlib, 0, btCipherIV.length);
System.arraycopy(encryptedData, 0, entlib, btCipherIV.length, encryptedData.length);
encryptedText = new String(encryptedData);
encryptedText = Base64.encodeBase64String(encryptedData);
return encryptedText;
} catch (Exception e) {
}
return "";
}
public static byte[] hexToBytes(String str) {
if (str==null) {
return null;
} else if (str.length() < 2) {
return null;
} else {
int len = str.length() / 2;
byte[] buffer = new byte[len];
for (int i=0; i<len; i++) {
buffer[i] = (byte) Integer.parseInt(
str.substring(i*2,i*2+2),16);
}
return buffer;
}
}
I found the answer. The problem in the above code:
StringUtils.getBytesUtf16(auth);
Instead the Enterprise Library is using Little Endian byte order. The function I was using doesn't. Instead I should have used:
StringUtils.getBytesUtf16Le(auth);
This solved my problem. Thanks for anyone who took a loot at this. I appreciate it!

Categories

Resources