I am trying to emulate same behaviour of Java in C#. But I am not getting desired result. Key generated by both is different.
JAVA CODE
public static String generateDecryptedKey(String secretKey, String authKey)
{
String strDecryptedKey = "";
byte[] salt = { (byte)0x09, (byte)0xD5, (byte)0xA1, (byte)0xA6, (byte)0xA3, (byte)0xA7, (byte)0xA9, (byte)0xA0 };
int iterationCount = 10;
KeySpec keySpec = new PBEKeySpec(secretKey.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
dcipher = Cipher.getInstance(key.getAlgorithm());
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
byte[] enc = Base64.decodeBase64(authKey.getBytes());
byte[] utf8 = dcipher.doFinal(enc);
strDecryptedKey = new String(utf8, "UTF-8");
return strDecryptedKey;
}
C# CODE
public static string generateDecryptedKey(string secretKey,string authKey)
{
string strDecryptedKey = string.Empty;
byte[] salt = { (byte)0x09, (byte)0xD5, (byte)0xA1, (byte)0xA6, (byte)0xA3, (byte)0xA7, (byte)0xA9, (byte)0xA0 };
int iterationCount = 10;
PKCSKeyGenerator kp = new PKCSKeyGenerator();
ICryptoTransform crypt = kp.Generate(secretKey, salt, iterationCount, 1);
var bytes = Encoding.UTF8.GetBytes(authKey);
byte[] resultBytes = crypt.TransformFinalBlock(bytes, 0, bytes.Length);
strDecryptedKey = Convert.ToBase64String(resultBytes);
return strDecryptedKey;
}
Result generated by both function upon same input is coming wrong. I am new to cryptography, please someone explain where I am doing wrong. Below is the link for class written by BobJanova that I am using in C# conversion.
https://www.codeproject.com/Articles/16450/Emulating-PBEWithMD-AndDES-Encryption-under-NET
NOTE: I don't want to reveal my SALT value so I had changed to some value. Hope you understand.
You should use
var bytes = Convert.FromBase64String(authKey);
instead of
var bytes = Encoding.UTF8.GetBytes(authKey);
and
strDecryptedKey = Encoding.UTF8.GetString(resultBytes);
instead of
strDecryptedKey = Convert.ToBase64String(resultBytes);
in C#.
Also, authKey.getBytes() in Java might lead to problems down the line. Always specify an encoding like authKey.getBytes("UTF-8").
Related
I am using one encryption/decryption method used in java and applying it in php . I have almost reached to the end but there is one mistake which I found that my decrypted string is not what i was expecting ,
This is my java code
private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
public static String encrypt(String message, String key) throws GeneralSecurityException, UnsupportedEncodingException {
if (message == null || key == null) {
throw new IllegalArgumentException("text to be encrypted and key should not be null");
}
Cipher cipher = Cipher.getInstance(ALGORITHM);
byte[] messageArr = message.getBytes();
byte[] keyparam = key.getBytes();
SecretKeySpec keySpec = new SecretKeySpec(keyparam, "AES");
byte[] ivParams = new byte[16];
byte[] encoded = new byte[messageArr.length + 16];
System.arraycopy(ivParams, 0, encoded, 0, 16);
System.arraycopy(messageArr, 0, encoded, 16, messageArr.length);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(ivParams));
byte[] encryptedBytes = cipher.doFinal(encoded);
encryptedBytes = Base64.getEncoder().encode(encryptedBytes);
return new String(encryptedBytes);
}
public static String decrypt(String encryptedStr, String key) throws GeneralSecurityException, UnsupportedEncodingException {
if (encryptedStr == null || key == null) {
throw new IllegalArgumentException("text to be decrypted and key should not be null");
}
Cipher cipher = Cipher.getInstance(ALGORITHM);
byte[] keyparam = key.getBytes();
SecretKeySpec keySpec = new SecretKeySpec(keyparam, "AES");
byte[] encoded = encryptedStr.getBytes();
encoded = Base64.getDecoder().decode(encoded);
byte[] decodedEncrypted = new byte[encoded.length - 16];
System.arraycopy(encoded, 16, decodedEncrypted, 0, encoded.length - 16);
byte[] ivParams = new byte[16];
System.arraycopy(encoded, 0, ivParams, 0, ivParams.length);
cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(ivParams));
byte[] decryptedBytes = cipher.doFinal(decodedEncrypted);
return new String(decryptedBytes);
}
now i have done this in php like this
$encKey = 'encryptionKey';
$cipher = "aes-256-cbc";
$data = 'some data';
$encryption_key = openssl_random_pseudo_bytes(32);
$iv_size = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($iv_size);
$ciphertext_raw = openssl_encrypt(json_encode($data), 'aes-256-cbc', $encKey, $options = OPENSSL_RAW_DATA, $iv);
//$ciphertext_raw = openssl_encrypt(json_encode($data), $cipher, $encKey, $options=OPENSSL_RAW_DATA, $iv);
$encrypted_data = base64_encode($iv . $ciphertext_raw);
print_r($encrypted_data);
now the problem is when i encrypt one string in php and decrypt it in java code it add some escape sequence but when i encrypt that same string in java and decrypt it in java it is proper and accurate and i am not sure why this is happening ... may be i am doing some thing wrong
as I have to do this encryption in php using java code i am not sure that is this process going in right way or there is some error .
this is the string which i am encrypting
String str = "vendor_id=INT_GTW&format=json&msg_code=KBEX99&data={\"header\":{\"msg_code\":\"KBEX99\",\"source\":\"INSTANTPAY\",\"channel\":\"CONBNK\",\"txn_ref_number\":\"INSTANTPAY_CONBNK_00001\",\"txn_datetime\":\"1498118309808\",\"ip\":\"1\",\"device_id\":\"XYWZPQR123\",\"api_version\":\"1.0.0\"},\"detail\":{\"entity\":\"INSTANTPAY\",\"intent\":\"REG\",\"user_identifier\":\"INSTANTPAY28423928\",\"crn\":\"105683710\",\"p1\":\"105683710\",\"p2\":\"\",\"p3\":\"INSTANTPAY\",\"p4\":\"\",\"p5\":\"\",\"p6\":\"\",\"p7\":\"\",\"p8\":\"\",\"p9\":\"\",\"p10\":\"\",\"p11\":\"\",\"p12\":\"\",\"p13\":\"\",\"p14\":\"\",\"p15\":\"\",\"p16\":\"\",\"p17\":\"\",\"p18\":\"\",\"p19\":\"\",\"p20\":\"\"}}";
and this is decrypted string
vendor_id=INT_GTW&format=json&msg_code=KBEX99&data={"header":{"msg_code":"KBEX99","source":"INSTANTPAY","channel":"CONBNK","txn_ref_number":"INSTANTPAY_CONBNK_00001","txn_datetime":"1498118309808","ip":"1","device_id":"XYWZPQR123","api_version":"1.0.0"},"detail":{"entity":"INSTANTPAY","intent":"REG","user_identifier":"INSTANTPAY28423928","crn":"105683710","p1":"105683710","p2":"","p3":"INSTANTPAY","p4":"","p5":"","p6":"","p7":"","p8":"","p9":"","p10":"","p11":"","p12":"","p13":"","p14":"","p15":"","p16":"","p17":"","p18":"","p19":"","p20":""}}
but when i am doing it in php and decrypting
"vendor_id=INT_GTW&format=json&msg_code=KBEX99&data={\"header\":{\"msg_code\":\"KBEX99\",\"source\":\"INSTANTPAY\",\"channel\":\"CONBNK\",\"txn_ref_number\":\"INSTANTPAY_CONBNK_00001\",\"txn_datetime\":\"1498118309808\",\"ip\":\"1\",\"device_id\":\"XYWZPQR123\",\"api_version\":\"1.0.0\"},\"detail\":{\"entity\":\"INSTANTPAY\",\"intent\":\"REG\",\"user_identifier\":\"INSTANTPAY28423928\",\"crn\":\"105683710\",\"p1\":\"105683710\",\"p2\":\"\",\"p3\":\"INSTANTPAY\",\"p4\":\"\",\"p5\":\"\",\"p6\":\"\",\"p7\":\"\",\"p8\":\"\",\"p9\":\"\",\"p10\":\"\",\"p11\":\"\",\"p12\":\"\",\"p13\":\"\",\"p14\":\"\",\"p15\":\"\",\"p16\":\"\",\"p17\":\"\",\"p18\":\"\",\"p19\":\"\",\"p20\":\"\"}}"
i am getting this
So any encryption decryption expert please help me in this ...
Thank you !!
Good afternoon. Interested in the question someone tried to decrypt data in Java that was encrypted in Ruby.
I'm trying to encrypt a word in Ruby (using the Marshal module) and decrypt in Java. If the Marshal module is used, can it be transferred to other languages or not?
This is my test in Ruby:
let(:key) { "12345678901234567890123456789012" }
let(:str) { "Serhii" }
it "encrypt_content" do
crypt = ActiveSupport::MessageEncryptor.new(key, cipher: 'aes-256-cbc')
encrypted_content = crypt.encrypt_and_sign(str)
encrypted_content
end
The code methods are:
def encrypt_and_sign(value, expires_at: nil, expires_in: nil, purpose: nil)
verifier.generate(_encrypt(value, expires_at: expires_at, expires_in: expires_in, purpose: purpose))
end
def _encrypt(value, **metadata_options)
cipher = new_cipher
cipher.encrypt
cipher.key = #secret
iv = cipher.random_iv
cipher.auth_data = "" if aead_mode?
encrypted_data = cipher.update(Messages::Metadata.wrap(#serializer.dump(value), metadata_options))
encrypted_data << cipher.final
blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"`enter code here`
blob = "#{blob}--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode?
blob
end
The decrypt Java is:
private static final String key = "12345678901234567890123456789012";
#SneakyThrows
public static String decrypt(String encrypted) {
byte[] firstByte = Base64.getDecoder().decode(encrypted.replaceAll("\n", "").getBytes(StandardCharsets.UTF_8));
String first = new String(firstByte);
String[] parts = first.split("--");
byte[] secondByte = Base64.getDecoder().decode(parts[0].getBytes(StandardCharsets.UTF_8));
String second = new String(secondByte);
String[] parts2 = second.split("--");
byte[] encryptedData = Base64.getDecoder().decode(parts2[0].getBytes(StandardCharsets.UTF_8));
SecretKeySpec aesKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, aesKey, new IvParameterSpec(new byte[16]));
byte[] result = cipher.doFinal(encryptedData);
return new String(result);
}
public static void main(String[] args) throws Exception {
String encrypted = "S3l0cVEybDRUM2sxU1hFMk5YVlhOa3A2VXpRNFEyZFFibTVwZVdRMVdEUlpN\n" +
"bkkxUzBaUGNsbFJaejB0TFRWWlVtVkNVWEJXZWxselJuWkVhbFJyWlU5VmNr\n" +
"RTlQUT09LS0yZDA5M2FhZTg0OTJjZmIyZjdiNDA0ZWVkNGU2ZmQ4NDQ1ZTM4\n" +
"ZjIx";
System.out.println("Decrypted: " + decrypt(encrypted));
}
}
Result �'��m�Qի���
What could be the reason?
The exact code that is produced by Ruby is not specified (which I would consider a bug), you can find the format by reading the source code, especially this part:
blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
blob = "#{blob}--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode?
Where the IV is a random IV, generated using Cipher::new of the openssl module.
I am trying to reproduce the following encryption/decryption algorithm in Java, but I can't find the alternatives for multiple methods such as Rfc2898DeriveBytes() and RijndaelManaged(). How do I do this?
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace CryptingTest
{
public static class StringCipher
{
// 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 = 128;
// 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 = Generate128BitsOfRandomEntropy();
var ivStringBytes = Generate128BitsOfRandomEntropy();
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = 128;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
// Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
var cipherTextBytes = saltStringBytes;
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
memoryStream.Close();
cryptoStream.Close();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
}
}
public static string Decrypt(string cipherText, string passPhrase)
{
// Get the complete stream of bytes that represent:
// [32 bytes of Salt] + [16 bytes of IV] + [n bytes of CipherText]
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
// Get the saltbytes by extracting the first 16 bytes from the supplied cipherText bytes.
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
// Get the IV bytes by extracting the next 16 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);
using (var symmetricKey = new RijndaelManaged())
{
symmetricKey.BlockSize = 128;
symmetricKey.Mode = CipherMode.CBC;
symmetricKey.Padding = PaddingMode.PKCS7;
using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
{
using (var memoryStream = new MemoryStream(cipherTextBytes))
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
var plainTextBytes = new byte[cipherTextBytes.Length];
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}
}
}
}
}
private static byte[] Generate128BitsOfRandomEntropy()
{
var randomBytes = new byte[16]; // 16 Bytes will give us 128 bits.
using (var rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with cryptographically secure random bytes.
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
}
}
Any help would be really appreciated.
Here's a code snippet from what I tried so far but it's still off:
private static byte[] Generate128BitsOfRandomEntropy()
{
var randomBytes = new byte[16]; // 16 Bytes will give us 128 bits.
SecureRandom rngCsp = new SecureRandom();
// Fill the array with cryptographically secure random bytes.
rngCsp.nextBytes(randomBytes);
return randomBytes;
}
public static String encrypt(String plainText, String passPhrase)
{
try
{
var saltStringBytes = Generate128BitsOfRandomEntropy();
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec pbeKeySpec = new PBEKeySpec(passPhrase.toCharArray(), saltStringBytes, 1000, 384);
Key secretKey = factory.generateSecret(pbeKeySpec);
byte[] key = new byte[16];
byte[] iv = new byte[16];
System.arraycopy(secretKey.getEncoded(), 0, key, 0, 16);
System.arraycopy(secretKey.getEncoded(), 16, iv, 0, 16);
SecretKeySpec secret = new SecretKeySpec(key, "AES");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret, ivSpec);
return Base64.getEncoder().encodeToString(cipher.doFinal(plainText.getBytes("UTF-8")));
}
catch (Exception e)
{
System.out.println("Error while encrypting: " + e.toString());
}
return null;
}
Rfc2898DeriveBytes implements PBKDF2, and RijndaelManaged with a block size of 128 bits implements AES. Both seem to be applied correctly in the Java code.
However, there are differences in determining the IV and regarding concatenation: In the C# code, the salt and IV are determined randomly and concatenated with the ciphertext at the end.
In the Java code only the salt is determined randomly, the IV is derived together with the key and the concatenation is missing.
I.e. the encrypt() method in the Java code could be changed for instance as follows, so that a decryption with the C# code is possible:
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
...
byte[] salt = Generate128BitsOfRandomEntropy();
byte[] iv = Generate128BitsOfRandomEntropy();
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec pbeKeySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, 1000, 128);
SecretKey secretKey = factory.generateSecret(pbeKeySpec);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
byte[] ciphertext = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
byte[] saltIvCiphertext = ByteBuffer.allocate(salt.length + iv.length + ciphertext.length).put(salt).put(iv).put(ciphertext).array();
return Base64.getEncoder().encodeToString(saltIvCiphertext);
Note that for PBKDF2, an iteration count of 1000 is generally too low.
I don't know why an error is coming up.
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
I understand that this error occurs when the incorrect key is used during the decryption. However, if you look at the test results result below, you can see that both C# and Java are the same (Key, IV, Salt is Base64 encoded).
C# Test Result
Java Test Result
It's the same!(Key, IV, Salt)
But the current BadpaddingException error is generated. What could be the problem?
I am attaching my source file.
C# (Encryption)
class AES {
private readonly static string keyStr = "This is Key";
private readonly static string vector = "This is Vector";
public static Rfc2898DeriveBytes MakeKey(string password){
byte[] keyBytes = System.Text.Encoding.UTF8.GetBytes(password);
byte[] saltBytes = SHA512.Create().ComputeHash(keyBytes);
Rfc2898DeriveBytes result = new Rfc2898DeriveBytes(keyBytes, saltBytes, 65536);
return result;
}
public static Rfc2898DeriveBytes MakeVector(string vector){
byte[] vectorBytes = System.Text.Encoding.UTF8.GetBytes(vector);
byte[] saltBytes = SHA512.Create().ComputeHash(vectorBytes);
Rfc2898DeriveBytes result = new Rfc2898DeriveBytes(vectorBytes, saltBytes, 65536);
return result;
}
public static void Encrypt(String inputFile, String outputFile) {
using (RijndaelManaged aes = new RijndaelManaged()){
//Create Key and Vector
Rfc2898DeriveBytes key = AES.MakeKey(AES.keyStr);
Rfc2898DeriveBytes vector = AES.MakeVector(AES.vector);
//AES256
aes.BlockSize = 128;
aes.KeySize = 256;
// It is equal in java
// Cipher _Cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = key.GetBytes(32); //256bit key
aes.IV = vector.GetBytes(16); //128bit block size
//processing Encrypt
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
byte[] encrypted;
using (MemoryStream msEncrypt = new MemoryStream()) {
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) {
byte[] inputBytes = File.ReadAllBytes(inputFile);
csEncrypt.Write(inputBytes, 0, inputBytes.Length);
}
encrypted = msEncrypt.ToArray();
}
string encodedString = Convert.ToBase64String(encrypted);
File.WriteAllText(outputFile, encodedString);
}
}
}
Java (Decryption)
public class AES256File {
private static final String algorithm = "AES";
private static final String blockNPadding = algorithm+"/CBC/PKCS5Padding";
private static final String password = "This is Key";
private static final String IV = "This is Vector";
private static IvParameterSpec ivSpec;
private static Key keySpec;
public static void MakeKey(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException{
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
MessageDigest digest = MessageDigest.getInstance("SHA-512");
byte[] keyBytes = password.getBytes("UTF-8");
// C# : byte[] saltBytes = SHA512.Create().ComputeHash(keyBytes);
byte[] saltBytes = digest.digest(keyBytes);
//256bit
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), saltBytes, 65536, 256);
Key secretKey = factory.generateSecret(pbeKeySpec);
byte[] key = new byte[32];
System.arraycopy(secretKey.getEncoded(), 0, key, 0, 32);
SecretKeySpec secret = new SecretKeySpec(key, "AES");
setKeySpec(secret);
}
public static void MakeVector(String IV) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException{
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
MessageDigest digest = MessageDigest.getInstance("SHA-512");
byte[] vectorBytes = IV.getBytes("UTF-8");
byte[] saltBytes = digest.digest(vectorBytes);
// 128bit
PBEKeySpec pbeKeySpec = new PBEKeySpec(IV.toCharArray(), saltBytes, 65536, 128);
Key secretIV = factory.generateSecret(pbeKeySpec);
byte[] iv = new byte[16];
System.arraycopy(secretIV.getEncoded(), 0, iv, 0, 16);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
setIvSpec(ivSpec);
}
public void decrypt(File source, File dest) throws Exception {
Cipher c = Cipher.getInstance(blockNPadding);
c.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
fileProcessing(source, dest, c);
}
public void fileProcessing(File source, File dest, Cipher c) throws Exception{
InputStream input = null;
OutputStream output = null;
try{
input = new BufferedInputStream(new FileInputStream(source));
output = new BufferedOutputStream(new FileOutputStream(dest));
byte[] buffer = new byte[input.available()];
int read = -1;
while((read = input.read(buffer)) != -1){
output.write(c.update(buffer, 0, read));
}
byte[] deryptedBytes = c.doFinal(buffer); // -----------------------> Error!! Showing!
byte[] decodedBytes = Base64.getDecoder().decode(deryptedBytes);
String decodeString = new String(decodedBytes, "UTF-8");
decodedBytes = decodeString.getBytes(StandardCharsets.UTF_8);
output.write(decodedBytes);
}finally{
if(output != null){
try{output.close();}catch(IOException e){}
}
if(input != null){
try{input.close();}catch(IOException e){}
}
}
}
I have verified as below.
Verification Key and IV in C#
//Key Verification
var salt = Convert.ToBase64String(saltBytes);
Console.Write("Salt Result : ");
Console.WriteLine(salt);
var result_test = Convert.ToBase64String(result.GetBytes(32));
Console.Write("Key Test Result: ");
Console.WriteLine(result_test);
//IV Verification (Salt is Using same code)
var result_test = Convert.ToBase64String(result.GetBytes(16));
Console.Write("IV Test Result: ");
Console.WriteLine(result_test);
Verification Key and IV in Java
//Key Verification
/* print Salt */
String base64 = Base64.getEncoder().encodeToString(saltBytes);
System.out.println("Salt Result : " + base64);
/* print Key */
String result_test = Base64.getEncoder().encodeToString(key);
System.out.println("Key Test Result : " + result_test);
/* print generated Key */
System.out.println("Secret Key Result : " + Base64.getEncoder().encodeToString(secret.getEncoded()));
//IV Verification (Salt is Using same code)
/* print IV */
String result_test = Base64.getEncoder().encodeToString(iv);
System.out.println("IV Test Result : " + result_test);
/* print generated IV */
System.out.println("IV Result : " + Base64.getEncoder().encodeToString(ivSpec.getIV()));
Updated
c# .netframework 4.5 / Java8 modified what #Topaco said and confirmed that it worked well.
I want to say thank you very much to #Topaco and #Gusto2, and I'm going to make changes to the parts that have been modified in security, just as #Gusto2 said!
1) In the C# Encrypt-method the plain text is encrypted first and then Base64-encoded. Thus, in the decryption process the data must be Base64-decoded first and then decrypted. Currently this is handled in the wrong order i.e. the data are decrypted first and then decoded. Therefore, in the Java fileProcessing-method replace
while((read = input.read(buffer)) != -1){
output.write(c.update(buffer, 0, read));
}
with
while((read = input.read(buffer)) != -1) {
byte[] bufferEncoded = buffer;
if (read != buffer.length) {
bufferEncoded = Arrays.copyOf(buffer, read);
}
byte[] bufferDecoded = Base64.getDecoder().decode(bufferEncoded);
output.write(c.update(bufferDecoded));
}
2) It's not necessary to pass buffer (or bufferDecoded) to the doFinal-method, since that was already done in the update-method. Thus,
byte[] deryptedBytes = c.doFinal(buffer);
must be replaced with
output.write(c.doFinal());
3) Since the Base64-decoding is already done in 1) in the try-block all lines following the doFinal-statement have to be removed. Overall, this results in
try {
input = new BufferedInputStream(new FileInputStream(source));
output = new BufferedOutputStream(new FileOutputStream(dest));
byte[] buffer = new byte[input.available()];
int read = -1;
while((read = input.read(buffer)) != -1) {
byte[] bufferEncoded = buffer;
if (read != buffer.length) {
bufferEncoded = Arrays.copyOf(buffer, read);
}
byte[] bufferDecoded = Base64.getDecoder().decode(bufferEncoded);
output.write(c.update(bufferDecoded));
}
output.write(c.doFinal());
}
4) The size of the buffer has to be a multiple of 4 in order to ensure a proper Base64-decoding. Thus, it's more reliable to replace
byte[] buffer = new byte[input.available()];
with
byte[] buffer = new byte[4 * (input.available() / 4)];
As long as the data are read in one chunk (which is not guaranteed, see e.g. https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/InputStream.html#available()) there is no problem. However, if the data are read in several chunks it's important to read a multiple of 4 bytes, otherwise the Base64-decoding will fail. That can be easily proved by using a buffer size which isn't a multiple of 4. This point must also be considered if the buffer size is explicitly defined with regard to larger files.
while((read = input.read(buffer)) != -1){
output.write(c.update(buffer, 0, read));
}
byte[] deryptedBytes = c.doFinal(buffer)
you are decrypting the input to a file, then you are using the same cipher instance to decrypt the the last read chunk (again) into a separate array not to the file
quick fix:
while((read = input.read(buffer)) != -1){
output.write(c.update(buffer, 0, read));
}
output.write(c.doFinal()); // write the padded block
if you want to create and print a decrypted String, you need to create a new Cipher instance (or maybe it will be enough to reinitialize the instance, I am not sure) assuming the buffer contains the whole input
c.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
// assuming the buffer contains the whole input again
byte[] deryptedBytes = c.doFinal(buffer); // decrypting the whole file again
correct approach:
IV is used to securely reuse the same encryption key for multiple encryptions. So if your key is not random, you should generate new random IV for each encryption (and pass the IV along the ciphertext, most often prepended). Otherwise the encryption is not semantically secure and you may create opening for the two pad attack. So deriving IV from the key may not be very secure.
I advice to use any MAC (authentication code) passed along the ciphertext to ensure integrity (e.g. HMAC)
you are still reading all the file input fully into memory, what would not work for REALLY LARGE files. You may initialize the buffer to an arbitrary length (a few MB?) and process the input file as chunked
The encrypted text is done in JAVA (which we have no JAVA background at all)
The decryption will be in C#, and here is the code
public static string DecryptString(string Message, string Passphrase)
{
byte[] Results;
UTF8Encoding UTF8 = new UTF8Encoding();
MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase));
// byte[] TDESKey = UTF8.GetBytes(Passphrase);
TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();
TDESAlgorithm.Key = TDESKey;
// TDESAlgorithm.Mode = CipherMode.CTS;
TDESAlgorithm.Padding = PaddingMode.Zeros;
byte[] DataToDecrypt = Convert.FromBase64String(Message);
try
{
ICryptoTransform Decryptor = TDESAlgorithm.CreateDecryptor();
Results = Decryptor.TransformFinalBlock(DataToDecrypt, 0, DataToDecrypt.Length);
}
finally
{
TDESAlgorithm.Clear();
HashProvider.Clear();
}
return Encoding.UTF8.GetString(Results);
}
Encrypted Java code is
public String encryptData(String privateKey, String rawData)
{
Cipher cipher = null;
try
{
cipher = Cipher.getInstance(DESEDE_ENCRYPTION_SCHEME);
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(privateKey));
byte[] plainText = rawData.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
return new String(Base64.encodeBase64(encryptedText));
}
}
However, when tried to decrypt, got the error message: BAD DATA
Where am I missing here?
You are not using MD5 in Java, so you should not be using it in your .NET for computing the hash.
Your key should have been generated using a specific encoding and same you should use in .NET.
Please note, there is some fundamental difference in java KeySpec and the Key being used for TripleDESCryptoServiceProvider. As mentioned by Microsfot https://msdn.microsoft.com/en-us/library/system.security.cryptography.tripledescryptoserviceprovider.aspx
Triple DES only supports "key lengths from 128 bits to 192 bits in increments of 64 bits"
So you need to convert your key appropriately before assigning. To do this you can use the Array.Resize method as following.
byte[] TDESKey = Encoding.UTF8.GetBytes(Passphrase);
System.Array.Resize(ref TDESKey , 192 / 8);
Hope this will help.