C# / Java | AES256 encrypt/decrypt - c#

I want to encrypt all the data I send through the Java/C# sockets (Java server, C# client).
I would like to use AES256, but I can't get the Java and C# to generate the same encrypted code. Can anyone give me two examples, 1 in Java and 1 in C# that generate the same results and decrypts the results properly?
What I tried so far:
public Encrypt(AOBCore instance){
try {
String message="This is just an example";
// Get the KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(256); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey(); //Cantget 'test' in here...
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted =
cipher.doFinal(message.getBytes());
System.out.println("encrypted string: " + asHex(encrypted));
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] original =
cipher.doFinal(encrypted);
String originalString = new String(original);
System.out.println("Original string: " +
originalString + " " + asHex(original));
} catch (Exception e) {
instance.logMessage(e.getMessage());
}
}
public static String asHex (byte buf[]) {
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10)
strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
}
static void Main(string[] args)
{
while (true)
{
var plain = Console.ReadLine();
var key = GenerateKey(256);
var encoded = Encrypt(plain, key, 256);
Console.WriteLine("Encoded: " + encoded);
Console.WriteLine(Decrypt(encoded, key, 256));
}
}
private static string GenerateKey(int keySize)
{
return "test";
}
private static string Encrypt(string plainStr, string completeEncodedKey, int keySize)
{
RijndaelManaged aesEncryption = new RijndaelManaged();
aesEncryption.KeySize = keySize;
aesEncryption.BlockSize = 256;
aesEncryption.Mode = CipherMode.CBC;
aesEncryption.Padding = PaddingMode.PKCS7;
aesEncryption.IV = Convert.FromBase64String(ASCIIEncoding.UTF8.GetString(Convert.FromBase64String(completeEncodedKey)).Split(',')[0]);
aesEncryption.Key = Convert.FromBase64String(ASCIIEncoding.UTF8.GetString(Convert.FromBase64String(completeEncodedKey)).Split(',')[1]);
byte[] plainText = ASCIIEncoding.UTF8.GetBytes(plainStr);
ICryptoTransform crypto = aesEncryption.CreateEncryptor();
// The result of the encryption and decryption
byte[] cipherText = crypto.TransformFinalBlock(plainText, 0, plainText.Length);
return Convert.ToBase64String(cipherText);
}
private static string Decrypt(string encryptedText, string completeEncodedKey, int keySize)
{
RijndaelManaged aesEncryption = new RijndaelManaged();
aesEncryption.KeySize = keySize;
aesEncryption.BlockSize = 128;
aesEncryption.Mode = CipherMode.CBC;
aesEncryption.Padding = PaddingMode.PKCS7;
aesEncryption.IV = Convert.FromBase64String(ASCIIEncoding.UTF8.GetString(Convert.FromBase64String(completeEncodedKey)).Split(',')[0]);
aesEncryption.Key = Convert.FromBase64String(ASCIIEncoding.UTF8.GetString(Convert.FromBase64String(completeEncodedKey)).Split(',')[1]);
ICryptoTransform decrypto = aesEncryption.CreateDecryptor();
byte[] encryptedBytes = Convert.FromBase64CharArray(encryptedText.ToCharArray(), 0, encryptedText.Length);
return ASCIIEncoding.UTF8.GetString(decrypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length));
}

The problem is that you aren't specifying the ciphermode or the padding in the Java code. This will use the algorithm defaults, which is never something you want to do when interoperability with other libraries is required. Initialize your Cipher like this:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
PKCS5 in Java should be compatible with PKCS7 in .Net according to this answer. Since you are wisely using CBC you are going to need to modify the code to use the same initialization vector for both encryption and decryption. You should NOT use the secret key for that. The IV should be randomly generated. You can use the IV that the Java Cipher generated for encryption by calling cipher.getIV().
Also, take care to be consistent with character encodings as has been mentioned in the comments.

Related

AES Encryption in C# and Decryption in JS using crypto-js

UseCase:
I am developing a service that is in C# and I have to send an encrypted string to a different system that is already coded in JS using CryptoJS which will be decrypting the string I am going to be sending.
The receiving system is not going to make any changes to its implementation. They have shared the JS code they are using to DECRYPT along with the required credentials that I would need for my encryption.
JS code of both Encrypt and Decrypt from the receiver
var message = "hello world!!!";
function generateKey(salt, passPhrase, keySize, iterationCount) {
const key = CryptoJS.PBKDF2(passPhrase, CryptoJS.enc.Hex.parse(salt), {
keySize: keySize / 32,
iterations: iterationCount
});
return key;
}
function Encrypt(cipherText) {
const salt = "DUMMYSALT-VALUE-of-Size-32CHARACTERS";
const iv ="DUMMYIV-VALUE-of-Size-32CHARACTERS";
const passPhrase = "SomeSecretPassPhrase";
const keySize = 128
const iterationCount = 1
const key = generateKey(salt, passPhrase, keySize, iterationCount);
const encrypted = CryptoJS.AES.encrypt(cipherText, key, {
iv: CryptoJS.enc.Hex.parse(iv)
});
return encrypted.ciphertext.toString();
}
function Decrypt(cipherText, pass = "SomeSecretPassPhrase") {
const salt = "DUMMYSALT-VALUE-of-Size-32CHARACTERS";
const iv ="DUMMYIV-VALUE-of-Size-32CHARACTERS";
const passPhrase = "SomeSecretPassPhrase";
const keySize = 128
const iterationCount = 1
const key = generateKey(salt, passPhrase, keySize, iterationCount);
const cipherParams = CryptoJS.lib.CipherParams.create({
ciphertext: CryptoJS.enc.Hex.parse(cipherText)
});
const decrypted = CryptoJS.AES.decrypt(cipherParams, key, {
iv: CryptoJS.enc.Hex.parse(iv)
});
return decrypted.toString(CryptoJS.enc.Utf8);
}
var encrypted = Encrypt(message);
//equivalent to CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(message), key);
var decrypted = Decrypt(encrypted);
$('#1').text("Encrypted: "+encrypted.toString(CryptoJS.enc.Utf8));
$('#2').text("Decrypted: "+decrypted.toString(CryptoJS.enc.Utf8));
Output
Encrypted: 24a935f11a3e0b35dd6604cd7dbee292
Decrypted: hello world!!!
Question: While I try to port this over the C# and try to encrypt a string using the below, my encrypted value is different and also the JS code is unable to decrypt it.
my code to encrypt:
var MyKey = GenerateKey("DUMMYSALT-VALUE-of-Size-32CHARACTERS", "SomeSecretPassPhrase");
var EncryptedString = EncryptString(MyKey, "hello world!!!");
public byte[] GenerateKey(string saltVal, string passphrase)
{
byte[] salt = ConvertHexStringToByteArray("DUMMYSALT-VALUE-of-Size-32CHARACTERS");
int iterations = 1;
var rfc2898 = new Rfc2898DeriveBytes(passphrase, salt, iterations);
byte[] key = rfc2898.GetBytes(16);
return key;
}
public string EncryptString(byte[] key, string stringToEncrypt )
{
AesManaged aesCipher = new AesManaged();
aesCipher.KeySize = 128;
aesCipher.BlockSize = 128;
aesCipher.Mode = CipherMode.CBC;
aesCipher.Padding = PaddingMode.PKCS7;
aesCipher.Key = key;
aesCipher.IV = ConvertHexStringToByteArray("DUMMYPRESETIV-VALUE-of-Size-32CHARACTERS");
byte[] b = System.Text.Encoding.UTF8.GetBytes(stringToEncrypt);
ICryptoTransform encryptTransform = aesCipher.CreateEncryptor();
byte[] ctext = encryptTransform.TransformFinalBlock(b, 0, b.Length);
System.Console.WriteLine("IV:" + Convert.ToBase64String(aesCipher.IV));
System.Console.WriteLine("Cipher text: " + Convert.ToBase64String(ctext));
return Convert.ToBase64String(ctext);
}
public byte[] ConvertHexStringToByteArray(string hexString)
{
int NumberChars = hexString.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
return bytes;
}
I am not sure if am doing the correct equivalent of the JS method CryptoJS.enc.Hex.parse()
,

Why is only one half of my encrypted string not decrypting properly?

Can't seem to figure this one out... I am using DESCryptoServiceProvider to do a quick little two way encryption (not security related, and security is not the purpose of this question).
Anyways it's weird because the string that goes in and then comes back out is only decrypting properly for one half of the string. I can't seem to notice the bug so maybe someone will have some fun with this...
I am combining the two strings with a colon as the separator so 'abc12345:xyz56789' is the input. Then notice in the output only the first part of the string is getting screwed up, not the second part. I would expect that if I was doing it totally wrong then the whole thing wouldn't decrypt properly.
Here is all the code:
class Program
{
static void Main(string[] args)
{
var userId = "abc12345";
var appId = "xyz56789";
Console.WriteLine($"UserId: {userId}, AppId: {appId}");
var code = QuickEncode(userId, appId);
Console.WriteLine(code);
var result = QuickDecode(code);
var uId = result.Item1;
var aId = result.Item2;
Console.WriteLine($"UserId: {uId}, AppId: {aId}");
Console.ReadKey();
}
private static string QuickEncode(string userId, string appId)
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
var desKey = StringToByteArray("437459133faf42cb");
des.Key = desKey;
ICryptoTransform encryptor = des.CreateEncryptor();
var encryptMe = $"{userId}:{appId}";
Console.WriteLine($"Input String: {encryptMe}");
byte[] stringBytes = System.Text.Encoding.UTF8.GetBytes(encryptMe);
byte[] enc = encryptor.TransformFinalBlock(stringBytes, 0, stringBytes.Length);
var encryptedBytesString = Convert.ToBase64String(enc);
return encryptedBytesString;
}
private static Tuple<string, string> QuickDecode(string code)
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
var desKey = StringToByteArray("437459133faf42cb");
des.Key = desKey;
ICryptoTransform decryptor = des.CreateDecryptor();
var codeBytes = Convert.FromBase64String(code);
byte[] originalAgain = decryptor.TransformFinalBlock(codeBytes, 0, codeBytes.Length);
var decryptMe = System.Text.Encoding.UTF8.GetString(originalAgain);
Console.WriteLine($"Output String: {decryptMe}");
var ids = decryptMe.Split(':');
return new Tuple<string, string>(ids[0], ids[1]);
}
public static string ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
public static byte[] StringToByteArray(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;
}
}
You must set initialization vector (IV) to the same value for encryption as well as for decryption. Because new IV is automatically generated for each new instance of DESCryptoServiceProvider, your IV differs and decryption is not successfull.
The reason that half of the message is decrypted correctly results from usage of CBC mode (which is default mode), which has one really nasty property, that only first block of encrypted message actually depends on value of IV, so potential attacker can decode all message, except first block, without knowing correct IV (of course, correct Key is still needed). So it is not recommended to use this mode. See Block cipher mode of operation for more info about this.
So solution is easy - store somewhere IV used for encryption and use the same IV for decryption. If possible, use another cypher mode too. Somthing like this:
using System;
using System.Security.Cryptography;
using System.Text;
class Program
{
static void Main(string[] args)
{
var userId = "abc12345";
var appId = "xyz56789";
Console.WriteLine($"UserId: {userId}, AppId: {appId}");
byte[] IV;
var code = QuickEncode(userId, appId, out IV);
Console.WriteLine(code);
var result = QuickDecode(code, IV);
var uId = result.Item1;
var aId = result.Item2;
Console.WriteLine($"UserId: {uId}, AppId: {aId}");
Console.ReadKey();
}
private static string QuickEncode(string userId, string appId, out byte[] IV)
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
var desKey = StringToByteArray("437459133faf42cb");
des.Key = desKey;
des.GenerateIV();
IV = des.IV;
ICryptoTransform encryptor = des.CreateEncryptor();
var encryptMe = $"{userId}:{appId}";
Console.WriteLine($"Input String: {encryptMe}");
byte[] stringBytes = System.Text.Encoding.UTF8.GetBytes(encryptMe);
byte[] enc = encryptor.TransformFinalBlock(stringBytes, 0, stringBytes.Length);
var encryptedBytesString = Convert.ToBase64String(enc);
return encryptedBytesString;
}
private static Tuple<string, string> QuickDecode(string code, byte[] IV)
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
var desKey = StringToByteArray("437459133faf42cb");
des.Key = desKey;
des.IV = IV;
ICryptoTransform decryptor = des.CreateDecryptor();
var codeBytes = Convert.FromBase64String(code);
byte[] originalAgain = decryptor.TransformFinalBlock(codeBytes, 0, codeBytes.Length);
var decryptMe = System.Text.Encoding.UTF8.GetString(originalAgain);
Console.WriteLine($"Output String: {decryptMe}");
var ids = decryptMe.Split(':');
return new Tuple<string, string>(ids[0], ids[1]);
}
public static string ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
public static byte[] StringToByteArray(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;
}
}

Decrypting data encrypted with AES-256-CBC in C# as with PHP

I did some encryption using PHP in my Database and would normally decrypt using:
$encrypt_method = "AES-256-CBC";
$secret_key = "testing";
$secret_iv = "testingyes!!!";
$key = hash('sha256', $secret_key); // hash the key
$iv = substr(hash('sha256', $secret_iv), 0, 16); // iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
echo(openssl_decrypt(base64_decode($data), $encrypt_method, $key, 0, $iv)); // the decrypted data
I'm trying to do the same task but with C# 2013 to decrypt the same data, any ideas?
I would encrypt in php using:
$encrypt_method = "AES-256-CBC";
$secret_key = "testing";
$secret_iv = "testingyes!!!";
$key = hash('sha256', $secret_key); // hash the key
$iv = substr(hash('sha256', $secret_iv), 0, 16); // iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
echo(base64_encode(openssl_encrypt($data, $encrypt_method, $key, 0, $iv))); // the encrypted data
encrypting: this is a test
gives: d0EzQ2MvMHkxRks2cXg5NkFkK2twZz09=
I tried this in C#:
public static String sha256_hash(String value)
{
StringBuilder Sb = new StringBuilder();
using (SHA256 hash = SHA256Managed.Create())
{
Encoding enc = Encoding.UTF8;
Byte[] result = hash.ComputeHash(enc.GetBytes(value));
foreach (Byte b in result)
Sb.Append(b.ToString("x2"));
}
return Sb.ToString();
}
private static String AES_decrypt(String Input)
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 256;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
aes.Key = Convert.FromBase64String(sha256_hash("testing"));
aes.IV = Convert.FromBase64String(sha256_hash("testingyes!!!").Substring(0, 16));
var decrypt = aes.CreateDecryptor();
byte[] xBuff = null;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write))
{
byte[] xXml = Convert.FromBase64String(Input);
cs.Write(xXml, 0, xXml.Length);
}
xBuff = ms.ToArray();
}
String Output = Encoding.UTF8.GetString(xBuff);
return Output;
}
string cipherData = "d0EzQ2MvMHkxRks2cXg5NkFkK2twZz09=";
string f = AES_decrypt(cipherData);
Console.Write(f);
But I'm getting error: specified key is not a valid size for this algorithm
However the key I'm using is working when I use PHP
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 256;
Block size should be 128 to be compatible with AES-256-CBC.
Rijndael supports variable block sizes - AES does not.

Aes Encrypt and Decrypt null adds bytes

Hi there i'm using this encryption method to encrypt my json value in .net side
public static string Encrypt256(string text)
{
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
aes.BlockSize = 128;
aes.KeySize = 256;
aes.IV = Encoding.UTF8.GetBytes(AesIV256);
aes.Key = Encoding.UTF8.GetBytes(AesKey256);
aes.Mode = CipherMode.CBC;
byte[] src = Encoding.Unicode.GetBytes(text);
using (ICryptoTransform encrypt = aes.CreateEncryptor())
{
byte[] dest = encrypt.TransformFinalBlock(src, 0, src.Length);
Debug.WriteLine(Convert.ToBase64String(dest));
return Convert.ToBase64String(dest);
}
}
And im trying to decrypt it in Node Js side
var crypto = require('crypto'),
algorithm = process.env.tombalaCryptoAlgorithm,
password = process.env.tombalaHmacPass,
iv = '!QAZ2WSX#EDC4RFV'
function encrypt(text) {
var cipher = crypto.createCipheriv(algorithm, password, iv)
var encrypted = cipher.update(text, 'utf8', 'base64')
encrypted += cipher.final('base64');
return encrypted;
You are converting your text to be encrypted to Unicode which means UTF-16.
In UTF-16 every character consists of two bytes. If the second byte is not used it is null as you have observed.
I assume you want UTF-8 encoding. Therefore replace the line
byte[] src = Encoding.Unicode.GetBytes(text);
with
byte[] src = Encoding.UTF8.GetBytes(text);

encryption result in java and .net are not same

I have a method in my .net project to encrypt a password
public string Encrypt(string plainText)
{
string PassPhrase = "#$^&*!#!$";
string SaltValue = "R#j#}{BAe";
int PasswordIterations = Convert.ToInt32(textBox5.Text); //amend to match java encryption iteration
string InitVector = "#1B2c3D4e5F6g7H8";
int KeySize = 256; //amend to match java encryption key size
byte[] initVectorBytes = Encoding.ASCII.GetBytes(InitVector);
byte[] saltValueBytes = Encoding.ASCII.GetBytes(SaltValue);
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
PasswordDeriveBytes password= new PasswordDeriveBytes(
PassPhrase,
saltValueBytes,
"MD5",
PasswordIterations);
byte[] keyBytes = password.GetBytes(KeySize / 8);
RijndaelManaged symmetricKey = new RijndaelManaged();
symmetricKey.Mode = CipherMode.CBC;
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
keyBytes,
initVectorBytes);
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream,
encryptor,
CryptoStreamMode.Write);
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
memoryStream.Close();
cryptoStream.Close();
string cipherText = Convert.ToBase64String(cipherTextBytes);
return cipherText;
}
I have been tasked to convert this method to java but in java I don't get the same result as the .Net version
My java code is
package com.andc.billing.pdc.security;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.management.openmbean.InvalidKeyException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class PasswordCrypto {
private static final String password = "#$^&*!#!$";
private static String initializationVector = "#1B2c3D4e5F6g7H8";
private static String salt = "R#j#}{BAe";
private static int pswdIterations = 2;
private static int keySize = 128;
private static final Log log = LogFactory.getLog(PasswordCrypto.class);
public static String encrypt(String plainText) throws
NoSuchAlgorithmException,
InvalidKeySpecException,
NoSuchPaddingException,
InvalidParameterSpecException,
IllegalBlockSizeException,
BadPaddingException,
UnsupportedEncodingException,
InvalidKeyException,
InvalidAlgorithmParameterException, java.security.InvalidKeyException, NoSuchProviderException
{
byte[] saltBytes = salt.getBytes("ASCII");//"UTF-8");
byte[] ivBytes = initializationVector.getBytes("ASCII");//"UTF-8");
// Derive the key, given password and salt.
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");//PBEWithMD5AndDES");
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(),
saltBytes,
pswdIterations,
keySize
);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //Cipher.getInstance("AES/CBC/PKCSPadding"
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(ivBytes));
byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("ASCII"));//UTF-8"));
String str=new org.apache.commons.codec.binary.Base64().encodeAsString(encryptedTextBytes);
log.info(str);
return str;
}
}
.net result of encryption of "1" is :
7mPh3/E/olBGbFpoA18oqw==
while java is
7RPk77AIKAhOttNLW4e5yQ==
Would you please help me solve this problem ?
First thing i've noticed is that the algorithms you are using are different, in .Net it's an extension of PBKDF1 and in java it's PBKDF2, PBKDF2 replaced PBKDF1.
In .net you are using the PasswordDeriveBytes class which "derives a key from a password using an extension of the PBKDF1 algorithm."
I also notice that the password iterations is hard-coded to 2 in Java and comes from a text box in .Net... ensure they are the same.
Correct that and let us know the outcome.
Update: For PBKDF2 in .net use the Rfc2898DeriveBytes class.
For some very good relevant information have a read of this page
EDIT: This link should be helpful and if you can use the Chilkat library
It's a complicated difference between 1 and 2, 1 is only supposed to do upto 20 bytes, MS has built an extension which allows more than that and the following code should reporduce the .net output more accurately. Taken from here.
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Hex;
public class PKCS5Test
{
/**
* #param args
*/
public static void main(String[] args) throws Exception
{
byte[] password = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
byte[] salt = PKCS5S1ParametersGenerator.PKCS5PasswordToBytes("MyTesting".toCharArray());
PKCS5S1ParametersGenerator generator = new PasswordDeriveBytes(new SHA1Digest());
generator.init(password, salt, 100);
byte[] key = ((KeyParameter)generator.generateDerivedParameters(512)).getKey();
System.out.println( "64 " + new String(Hex.encode(key)).toUpperCase() );
}
static class PasswordDeriveBytes extends PKCS5S1ParametersGenerator
{
private final Digest d;
private byte[] output = null;
public PasswordDeriveBytes(Digest d)
{
super(d);
this.d = d;
}
public CipherParameters generateDerivedParameters(int keySize)
{
keySize = keySize / 8;
byte[] result = new byte[keySize];
int done = 0;
int count = 0;
byte[] b = null;
while (done < result.length)
{
if (b == null)
{
b = generateInitialKey();
}
else if (++count < 1000)
{
b = generateExtendedKey(++count);
}
else
{
throw new RuntimeException("Exceeded limit");
}
int use = Math.min(b.length, result.length - done);
System.arraycopy(b, 0, result, done, use);
done += use;
}
return new KeyParameter(result);
}
private byte[] generateOutput()
{
byte[] digestBytes = new byte[d.getDigestSize()];
d.update(password, 0, password.length);
d.update(salt, 0, salt.length);
d.doFinal(digestBytes, 0);
for (int i = 1; i < (iterationCount - 1); i++)
{
d.update(digestBytes, 0, digestBytes.length);
d.doFinal(digestBytes, 0);
}
return digestBytes;
}
private byte[] generateInitialKey()
{
output = generateOutput();
d.update(output, 0, output.length);
byte[] digestBytes = new byte[d.getDigestSize()];
d.doFinal(digestBytes, 0);
return digestBytes;
}
private byte[] generateExtendedKey(int count)
{
byte[] prefix = Integer.toString(count).getBytes();
d.update(prefix, 0, prefix.length);
d.update(output, 0, output.length);
byte[] digestBytes = new byte[d.getDigestSize()];
d.doFinal(digestBytes, 0);
//System.err.println( "X: " + new String(Hex.encode(digestBytes)).toUpperCase() );
return digestBytes;
}
}
}
Thank you very much for the provided solution - it works very well but with a small correction (according to initial post mentioned below):
Please use:
b = generateExtendedKey(count);
instead of:
b = generateExtendedKey(++count);
It'll work even for 256 key size:
Here is a small code which decrypts C# Rijndael encoded data using 256 bits keys:
public static String decrypt(final String cipherText, final String passPhrase, final String saltValue, final int passwordIterations, final String initVector, final int keySize)
throws Exception {
final byte[] initVectorBytes = initVector.getBytes("ASCII");
final byte[] saltValueBytes = saltValue.getBytes("ASCII");
final byte[] cipherTextBytes = Base64.decode(cipherText);
final PKCS5S1ParametersGenerator generator = new PasswordDeriveBytes(new SHA1Digest());
generator.init(passPhrase.getBytes("ASCII"), saltValueBytes, passwordIterations);
final byte[] key = ((KeyParameter) generator.generateDerivedParameters(keySize)).getKey();
final SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);
final Cipher cipher = Cipher.getInstance(TRANSFORMATION);
final IvParameterSpec iv = new IvParameterSpec(initVectorBytes);
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
final byte[] decryptedVal = cipher.doFinal(cipherTextBytes);
return new String(decryptedVal);
}
Addon:
In case you care about key size limitation, you may use this solution which works just fine (tested under Ubuntu 12, Java 1.7 64 bits (java version "1.7.0_25"
Java(TM) SE Runtime Environment (build 1.7.0_25-b15)
Java HotSpot(TM) 64-Bit Server VM (build 23.25-b01, mixed mode))

Categories