I have tried several attempts to port this code over to C# but I can not seem to get the outputs to match. Can someone help point me in the direction of what I am missing. I have included the actual code as well as links to online tools to run the samples and see the results.
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
public class JavaEncryption {
private static SecretKeySpec secretKey;
private static byte[] key;
private static void processEncryptionKey(String secret)
{
try
{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(secret.getBytes("UTF-8"));
byte[] base_hash = new byte[20];
base_hash = crypt.digest();
byte[] buffer = new byte[64];
for (int i = 0; i < 64; i ) buffer [i] = 0x36;
for (int i = 0; i < 20; i ) buffer [i] ^= base_hash[i];
crypt.reset();
crypt.update(buffer, 0, buffer.length);
byte[] final_hash = new byte[20];
final_hash = crypt.digest();
key = Arrays.copyOf(final_hash, 16);
secretKey = new SecretKeySpec(key, "AES");
}
catch(NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch(UnsupportedEncodingException e)
{
e.printStackTrace();
}
}
public static String encrypt(String strToEncrypt, String secret)
{
try
{
processEncryptionKey(secret);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(new byte[16]));
return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
}
catch (Exception e)
{
System.out.println("Error while encrypting: " e.toString());
}
return null;
}
public static String decrypt(String strToDecrypt, String secret)
{
try
{
processEncryptionKey(secret);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(new byte[16]));
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
}
catch (Exception e)
{
System.out.println("Error while decrypting: " e.toString());
}
return null;
}
public static void main(String[] args){
final String secretKey = "YVr_wlp8_fFa";
System.out.println("Secret = " secretKey);
String originalString = "testing";
System.out.println("Clear text = " originalString);
String encryptedString = JavaEncryption.encrypt(originalString, secretKey) ;
System.out.println("Encrypted = " encryptedString);
String decryptedString = JavaEncryption.decrypt(encryptedString, secretKey);
System.out.println("Decrypted = " decryptedString);
}
}
Here is the C# code I am trying to use but not getting the same result. I'm hoping it is just something small that I am missing:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
public class CSharpEncryption
{
public static void Main()
{
Console.WriteLine(new CSharpEncryption().EncryptText());
}
public string EncryptText()
{
using (var aes = new AesManaged())
{
aes.Key = CreateSecretKey();
aes.IV = new byte[16];
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
byte[] encrypted = GetEncryptedBytes("testing", aes.Key, aes.IV);
string encString = Convert.ToBase64String(encrypted);
return encString;
}
}
protected byte[] GetEncryptedBytes(string textToEncrypt, byte[] Key, byte[] IV)
{
byte[] encrypted;
using (var rij = new RijndaelManaged())
{
rij.Key = Key;
rij.IV = IV;
ICryptoTransform encryptor = rij.CreateEncryptor(rij.Key, rij.IV);
using (var msEncrypt = new MemoryStream())
{
using (var cs = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (var sw = new StreamWriter(cs))
{
sw.Write(textToEncrypt);
}
encrypted = msEncrypt.ToArray();
}
}
}
return encrypted;
}
private static byte[] CreateSecretKey()
{
var keyHash = CreateHashedKey();
byte[] key = null;
using (Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(keyHash, new byte[16], 1))
{
key = rfc2898.GetBytes(16);
}
return key;
}
private static string CreateHashedKey()
{
var hashBase64 = String.Empty;
using (SHA1Managed sha1 = new SHA1Managed())
{
byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes("YVr_wlp8_fFa"));
hashBase64 = CreateBase64Hash(hash, true);
}
return hashBase64;
}
private static string CreateBase64Hash(byte[] arr, bool withoutPadding)
{
var base64String = Convert.ToBase64String(arr);
if (withoutPadding)
{
base64String = base64String.TrimEnd('=');
}
base64String = "\n";
return base64String;
}
}
https://dotnetfiddle.net/ktULKG
CodePudding user response:
Both codes differ in the key derivation. For instance, the C# code uses Rfc2898DeriveBytes
, which implements PBKDF2. But there is nothing of this in the Java code. So you just have to port the logic of processEncryptionKey()
from the Java code to C#, e.g.:
private static byte[] ProcessEncryptionKey(string secret)
{
using (SHA1Managed sha1 = new SHA1Managed())
{
byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(secret));
byte[] extHash = new byte[64];
for (int i = 0; i < 64; i ){
extHash[i] = (byte)(i < 20 ? hash[i]^0x36 : 0x36);
}
byte[] finHash = sha1.ComputeHash(extHash);
byte[] secretKey = new byte[16];
Buffer.BlockCopy(finHash, 0, secretKey, 0, secretKey.Length);
return secretKey;
}
}
which is called with aes.Key = ProcessEncryptionKey("YVr_wlp8_fFa")
in EncryptText()
. This gives the same ciphertext as the Java code.
Be aware that the code has vulnerabilities:
- As key derivation, a dedicated key derivation function such as PBKDF2 should be used.
- A static IV (like a zero IV) is insecure.