Encrypt from C# just like SQL Server EncryptByPassPhrase? - c#

After following the answer of this question, I was able to recreate the DecryptByPassPhrase function of SQL. Now I am trying to do the EncryptByPassPhrase function.
We have different SQL version on different server which cannot be upgrade to the same version at the same time, so we want to create our c# function (with SQL CLR).
I need to encode with SHA1 (like a SQL 2012) since the SQL on the other server might not be upgraded to 2017 yet
Here is my c#, the function compile, but I can't decrypt it with my other c# function (which is exactly the function of the answer of the other question)
int keySize = 16;
var cryptoAlgo = TripleDES.Create();
cryptoAlgo.Padding = PaddingMode.PKCS7;
cryptoAlgo.Mode = CipherMode.CBC;
cryptoAlgo.GenerateIV(); //cryptoAlgo.IV = StringToByteArray(value.Substring(8, 16));
//cryptoAlgo.IV = StringToByteArray("7854E155CEE338D5");
var valueInByte = Encoding.Unicode.GetBytes(value); //UTF8Encoding.UTF8.GetBytes(value); //encrypted = StringToByteArray(value.Substring(24));
byte[] passwordBytes = Encoding.Unicode.GetBytes(password);
var hashAlgo = SHA1.Create();
hashAlgo.TransformFinalBlock(passwordBytes, 0, passwordBytes.Length);
cryptoAlgo.Key = hashAlgo.Hash.Take(keySize).ToArray();
byte[] encrypted = cryptoAlgo.CreateEncryptor().TransformFinalBlock(valueInByte, 0, valueInByte.Length);
//byte[] encryptedData = encrypted.Skip(8).ToArray();
//bool isUtf16 = (Array.IndexOf(encryptedData, (byte)0) != -1);
//string encryptedText = (isUtf16 ? Encoding.Unicode.GetString(encryptedData) : Encoding.UTF8.GetString(encryptedData));
return new SqlString(encryptedText);
//return new SqlString(encryptedText);
//return Convert.ToBase64String(encrypted, 0, encrypted.Length);
//return Convert.ToBase64String(encryptedData, 0, encryptedData.Length);
//return "0x01000000" + Convert.ToBase64String(cryptoAlgo.IV, 0, cryptoAlgo.IV.Length) + Convert.ToBase64String(encrypted, 0, encrypted.Length);
//return "0x01000000" + Convert.ToBase64String(encryptedData, 0, encryptedData.Length);
there a lot of different return that i've tried (they are in comment). I noticed that SQL return "0x01000000" and I've guess that the next 16 character are the IV so i've tried to add them, with no luck

Had a similar requirement, note that simply encrypting the data is not enough, your final byte array should be structured as follows:
First four bytes are the version: 2 0 0 0
Next 16 bytes are the
initialization vector: cryptoAlgo.IV
The remainder is the encrypted data, structured (prior to encrypting) as follows:
First
four bytes are the Magic value: 13, 240, 173, 186 (0xbaadf00d)
Next 2
bytes are 0
Next 2 bytes are the length of the value bytes
The remainder is the value bytes
Code:
public static class SqlCryptographyExtensions
{
const uint MagicPrefix = 0xbaadf00d;
public static byte[] EncryptByPassPhrase(this string value, string passPhrase)
{
var keyBytes = Encoding.Unicode.GetBytes(passPhrase);
// Depending on whether you're working with NVARCHAR/VARCHAR on SQL Server, use Unicode/UTF encoding here
var valueBytes = Encoding.UTF8.GetBytes(value);
var payload = new List<byte>();
payload.AddRange(BitConverter.GetBytes(MagicPrefix));
payload.AddRange(BitConverter.GetBytes((UInt16)0));
payload.AddRange(BitConverter.GetBytes((UInt16)valueBytes.Length));
payload.AddRange(valueBytes);
var payloadBytes = payload.ToArray();
HashAlgorithm hash = SHA256.Create();
SymmetricAlgorithm encryption = Aes.Create();
encryption.GenerateIV();
encryption.Padding = PaddingMode.PKCS7;
encryption.Mode = CipherMode.CBC;
hash.TransformFinalBlock(keyBytes, 0, keyBytes.Length);
encryption.Key = hash.Hash.Take(32).ToArray();
byte[] encryptedPayload = encryption.CreateEncryptor().TransformFinalBlock(payloadBytes, 0, payloadBytes.Length);
byte[] version = new byte[] { 2, 0, 0, 0 };
var encryptedBytes = new List<byte>();
encryptedBytes.AddRange(version);
encryptedBytes.AddRange(encryption.IV);
encryptedBytes.AddRange(encryptedPayload);
return encryptedBytes.ToArray();
}
}

Related

How to properly call openssl_decrypt method if I have base64 string which contains from IV and encrypted data?

I have code in C# which encrypts and decrypts string:
private readonly UTF8Encoding _encoder;
private readonly ICryptoTransform _encryptor;
private readonly RijndaelManaged _rijndael;
public Crypter()
{
_rijndael = new RijndaelManaged { Key = { 1, 2, 3, 4, ..., 16 } };
_rijndael.GenerateIV();
_encryptor = _rijndael.CreateEncryptor();
_encoder = new UTF8Encoding();
}
public string Encrypt(string unencrypted)
=> Convert.ToBase64String(Encrypt(_encoder.GetBytes(unencrypted)));
private byte[] Encrypt(byte[] buffer)
{
byte[] inputBuffer = _encryptor.TransformFinalBlock(buffer, 0, buffer.Length);
return _rijndael.IV.Concat(inputBuffer).ToArray();
}
public string Decrypt(string encrypted)
=> _encoder.GetString(Decrypt(Convert.FromBase64String(encrypted)));
private byte[] Decrypt(byte[] buffer)
{
byte[] iv = buffer.Take(16).ToArray();
using (ICryptoTransform decryptor = _rijndael.CreateDecryptor(_rijndael.Key, iv))
{
return decryptor.TransformFinalBlock(buffer, 16, buffer.Length - 16);
}
}
If you check Decrypt(byte[] buffer), I take first 16 bytes which is IV.
Now I similar want to implement in PHP (imagine, that I will encode on C# side and send it to server which runs on php, where I want to decrypt it back). As the param to my PHP decrypt function will be output of C# public string Encrypt(string unencrypted). I somehow need to get those 16 bytes to get IV and the rest part, which I respectively will pass as $data and $iv params to $decrypted_data = openssl_decrypt($data, $cipher, $encryption_key, 0, $iv); function
I have tried to use something like this (using unpack):
$stringValue = base64_decode($encrypted_data, true);
$integers = unpack("s*", $stringValue);
and then tried to take 16 first numbers and somehow convert them back with pack method. But probably I have lack of knowledge.
Could you please help me with this?
P.S. This one I have tried based on Ilya's answer and comments.
$cipher = "aes-256-cbc";
$encryption_key = hex2bin(env("ENCRYPTION_KEY"));
$base64decoded = base64_decode($encrypted_data, true);
$iv = substr($base64decoded, 0, 16);
$data = substr($base64decoded, 16, strlen($base64decoded) - 16);
$decrypted_data = openssl_decrypt($data, $cipher, $encryption_key, OPENSSL_RAW_DATA, $iv);
dd($decrypted_data);
also if I debug code and check which bytes are in $iv using this code:
$stringValue = base64_decode($iv, true);
$integers = unpack("C*", $encrypted_data);
and comparing with C# this byte array byte[] iv = buffer.Take(16).ToArray(); , they are equals, then I expect, that I am using wrongly openssl_decrypt method
In php any string is just a sequence of bytes, so you can work with it directly, e.g. access single byte by its index, or use substr to trim some amount of bytes. Example:
$str = 'some text or binary data received by http';
$first16Bytes = substr($str, 0, 16);
Firstly, I was having an issue how to correct retrieve 16 first bytes from string, because I thought I was doing it incorrectly. Thanks from #Ilia Yatsenko for his answer:
$first16Bytes = substr($str, 0, 16);
But then I have realised, that I am wrongly using openssl_decrypt() method. After having conversation in comments, particularly with #Topaco, we found what was the proble. Here is working code:
$cipher = "aes-256-cbc";
$encryption_key = hex2bin(env("ENCRYPTION_KEY"));
$base64decoded = base64_decode($encrypted_data, true);
$iv = substr($base64decoded, 0, 16);
$data = substr($base64decoded, 16, strlen($base64decoded) - 16);
$decrypted_data = openssl_decrypt($data, $cipher, $encryption_key,
OPENSSL_RAW_DATA, $iv);
dd($decrypted_data);

How to handle encrypt and decrypt file in ASP.NET core web API

What is the best way to implement encrypt and decrypt file content using asp.net core web API?
It should be compatible with the swagger also. I have tried this the same way as encrypt a string, but there is a limitation of size length or incomplete file sent over API.
I require to encrypt at the client end before API call via swagger or postman and be decrypt at service end.
File content encrypts at the client end then only data at transmission will safe.
I have tried below
public static string encrypt(string PlainText, byte[] key, byte[] iv)
{
string sR = string.Empty;
byte[] plainBytes = Encoding.UTF8.GetBytes(PlainText);
GcmBlockCipher cipher = new GcmBlockCipher(new AesFastEngine());
AeadParameters parameters =
new AeadParameters(new KeyParameter(key), 128, iv, null);
cipher.Init(true, parameters);
byte[] encryptedBytes = new byte[cipher.GetOutputSize(plainBytes.Length)];
Int32 retLen = cipher.ProcessBytes
(plainBytes, 0, plainBytes.Length, encryptedBytes, 0);
cipher.DoFinal(encryptedBytes, retLen);
sR = Convert.ToBase64String(encryptedBytes, Base64FormattingOptions.None);
return sR;
}
You can take a look at this link: https://github.com/2Toad/Rijndael256/issues/13#issuecomment-637724412
It uses the Rijndael/AES cypher to encrypt the data you put in.
In the Github issue you will find the following code:
public static class CipherHelper
{
// This constant is used to determine the keysize of the encryption algorithm in bits.
// We divide this by 8 within the code below to get the equivalent number of bytes.
private const int Keysize = 256;
// This constant determines the number of iterations for the password bytes generation function.
private const int DerivationIterations = 1000;
public static string Encrypt(string plainText, string passPhrase)
{
// Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
// so that the same Salt and IV values can be used when decrypting.
var saltStringBytes = Generate256BitsOfRandomEntropy();
var ivStringBytes = Generate256BitsOfRandomEntropy();
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
var engine = new RijndaelEngine(256);
var blockCipher = new CbcBlockCipher(engine);
var cipher = new PaddedBufferedBlockCipher(blockCipher, new Pkcs7Padding());
var keyParam = new KeyParameter(keyBytes);
var keyParamWithIV = new ParametersWithIV(keyParam, ivStringBytes, 0, 32);
cipher.Init(true, keyParamWithIV);
var comparisonBytes = new byte[cipher.GetOutputSize(plainTextBytes.Length)];
var length = cipher.ProcessBytes(plainTextBytes, comparisonBytes, 0);
cipher.DoFinal(comparisonBytes, length);
// return Convert.ToBase64String(comparisonBytes);
return Convert.ToBase64String(saltStringBytes.Concat(ivStringBytes).Concat(comparisonBytes).ToArray());
}
}
public static string Decrypt(string cipherText, string passPhrase)
{
// Get the complete stream of bytes that represent:
// [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
// Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
// Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
// Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
var engine = new RijndaelEngine(256);
var blockCipher = new CbcBlockCipher(engine);
var cipher = new PaddedBufferedBlockCipher(blockCipher, new Pkcs7Padding());
var keyParam = new KeyParameter(keyBytes);
var keyParamWithIV = new ParametersWithIV(keyParam, ivStringBytes, 0, 32);
cipher.Init(false, keyParamWithIV);
var comparisonBytes = new byte[cipher.GetOutputSize(cipherTextBytes.Length)];
var length = cipher.ProcessBytes(cipherTextBytes, comparisonBytes, 0);
cipher.DoFinal(comparisonBytes, length);
//return Convert.ToBase64String(saltStringBytes.Concat(ivStringBytes).Concat(comparisonBytes).ToArray());
var nullIndex = comparisonBytes.Length - 1;
while (comparisonBytes[nullIndex] == (byte)0)
nullIndex--;
comparisonBytes = comparisonBytes.Take(nullIndex + 1).ToArray();
var result = Encoding.UTF8.GetString(comparisonBytes, 0, comparisonBytes.Length);
return result;
}
}
private static byte[] Generate256BitsOfRandomEntropy()
{
var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.
using (var rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with cryptographically secure random bytes.
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
}
To make this code work, you will need to install the BouncyCastle.NetCore NuGet package.
This code helps you to encrypt and decrypt a string by using a password.
In your case, if you somehow now the password at both sides of the request (send-side and receive-side) than this should be able to work.

BouncyCastle decrypt output size incorrect

I am using BouncyCastle.NetCore. At the moment of decryption, it seems the GetOutputSize call returns a larger than the actual required length for the output byte array, with the extra bytes having value '0x00'.
Before converting it back to objects or strings, I hence need to recreate the byte array with the actual length that I retrieve from the ProcessBytes and DoFinal calls.
Should I be using a different call to get the correct number of bytes beforehand or how should I go about this?
public AesEncryptor()
{
// AES - CBC - with default PKCS5/PKCS7 scheme
_encryptionCipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(new AesEngine()));
_decryptionCipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(new AesEngine()));
}
public byte[] Encrypt(byte[] iv, byte[] byteArrayToEncrypt)
{
ParametersWithIV keyParamWithIV = new ParametersWithIV(_keyParam, iv, 0, iv.Length);
byte[] encryptedBytes;
lock (_encryptionLock)
{
_encryptionCipher.Init(true, keyParamWithIV);
encryptedBytes = new byte[_encryptionCipher.GetOutputSize(byteArrayToEncrypt.Length)];
int length = _encryptionCipher.ProcessBytes(byteArrayToEncrypt, encryptedBytes, 0);
_encryptionCipher.DoFinal(encryptedBytes, length);
}
return encryptedBytes;
}
public byte[] Decrypt(byte[] iv, byte[] byteArrayToDecrypt)
{
ParametersWithIV keyParamWithIV = new ParametersWithIV(_keyParam, iv, 0, iv.Length);
byte[] decryptedBytesReworked;
lock (_decryptionLock)
{
_decryptionCipher.Init(false, keyParamWithIV);
var decryptedBytes = new byte[_decryptionCipher.GetOutputSize(byteArrayToDecrypt.Length)];
int length = _decryptionCipher.ProcessBytes(byteArrayToDecrypt, decryptedBytes, 0);
int newLength = _decryptionCipher.DoFinal(decryptedBytes, length); //Do the final block
// TODO - incorrect initial byte array length
decryptedBytesReworked = new byte[length + newLength];
Array.Copy(decryptedBytes, decryptedBytesReworked, decryptedBytesReworked.Length);
}
return decryptedBytesReworked;
}
Cheers.

AES decryption with invalid length

I have server-client. Server encrypt message and send to client.
Client want to decrypt, but program is crash and error say: Length of the data to decrypt is invalid.
Here is code, where I listener server.
while (true)
{
if (tok.CanRead)
{
tok = client.GetStream();
int buffSize = 0;
byte[] inStream = new byte[10025];
tok.Read(inStream, 0, inStream.Length);
RijndaelManaged AesEncryption = new RijndaelManaged();
AesEncryption.KeySize = 256; // 192, 256
AesEncryption.BlockSize = 128;
AesEncryption.Mode = CipherMode.CBC;
AesEncryption.Padding = PaddingMode.PKCS7;
string keyStr = "cGFzc3dvcmQAAAAAAAAAAA==";
string ivStr = "cGFzc3dvcmQAAAAAAAAAAA==";
byte[] ivArr = Convert.FromBase64String(keyStr);
byte[] keyArr = Convert.FromBase64String(ivStr);
AesEncryption.IV = ivArr;
AesEncryption.Key = keyArr;
ICryptoTransform decrypto = AesEncryption.CreateDecryptor();
inStream = decrypto.TransformFinalBlock(inStream, 0, inStream.Length); //here is error
string returndata = System.Text.Encoding.ASCII.GetString(inStream);
readdata = "" + returndata;
msg();
}
}
How can I fix that length?.
The .NET API is build around streams. Try and use the CryptoStream API instead, initialized with an AesCryptoServiceProvider. TransformFinalBlock should only be used for low level access, and only for the last bytes of the ciphertext.
Quote:
TransformFinalBlock is a special function for transforming the last block or a partial block in the stream. It returns a new array that contains the remaining transformed bytes. A new array is returned, because the amount of information returned at the end might be larger than a single block when padding is added.

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