I have C# code which decrypts encrypted token passed by another application. I can not change this part. Now i'm writing an application in java which will encrypt my token, that would be passed to C# application.
I'm not able to match encrypted string with java code. Any help would be appreciated. Thank you.
C# Code
public class Crypto
{
private TripleDES DesInstance = null;
public Crypto(string key)
{
byte[] password = Encoding.GetEncoding(1252).GetBytes(key);
DesInstance = new System.Security.Cryptography.TripleDESCryptoServiceProvider();
PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, null);
DesInstance.IV = new byte[8];
DesInstance.Key = pdb.CryptDeriveKey("TripleDES", "SHA1", 192, DesInstance.IV);
}
public string Decrypt(string cipheredText)
{
byte[] cipherText = StringToByteArray(cipheredText);
string plainText = null;
ICryptoTransform transform = DesInstance.CreateDecryptor();
MemoryStream memStreamEncryptedData = new MemoryStream(cipherText, 0, cipherText.Length - 1);
CryptoStream encStream = new CryptoStream(memStreamEncryptedData, transform, CryptoStreamMode.Read);
using (StreamReader srDecrypt = new StreamReader(encStream, Encoding.GetEncoding(1252)))
{
plainText = srDecrypt.ReadToEnd();
}
return plainText;
}
private byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
}
Java Code
public class TripleDes {
private static final String UNICODE_FORMAT = "UTF-8";
public static final String DESEDE_ENCRYPTION_SCHEME = "DESede";
private KeySpec ks;
private SecretKeyFactory skf;
private Cipher cipher;
byte[] arrayBytes;
private String myEncryptionKey;
private String myEncryptionScheme;
SecretKey key;
public TripleDes() throws Exception {
myEncryptionKey = "045e466ccc34a1f1688640d0441601b7ae2c";
myEncryptionScheme = DESEDE_ENCRYPTION_SCHEME;
arrayBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
ks = new DESedeKeySpec(arrayBytes);
skf = SecretKeyFactory.getInstance(myEncryptionScheme);
cipher = Cipher.getInstance(myEncryptionScheme);
key = skf.generateSecret(ks);
}
public String encrypt(String unencryptedString) {
String encryptedString = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
encryptedString = new String(Base64.encodeBase64(encryptedText));
} catch (Exception e) {
e.printStackTrace();
}
return encryptedString;
}
public String decrypt(String encryptedString) {
String decryptedText = null;
try {
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedText = Base64.decodeBase64(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText = new String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
}
The C# code uses
PasswordDeriveBytes#CryptDeriveKey()as key derivation function which is a wrapper around CAPI'sCryptDeriveKey()function (see here). This functionality is missing in the Java code and is one of the reasons for the incompatibility.PasswordDeriveBytes#CryptDeriveKey()is not to be confused withPasswordDeriveBytes#GetBytes(). The latter uses the (by MS) extended implementation of the PBKDF1 algorithm mentioned in the comments, which is not applied here.The algorithm of
CryptDeriveKey()is described here and an implementation can be found here:Note that
CryptDeriveKey()does not expect the passphrase as first parameter, but the hash of the passphrase.Example of usage:
Note that in the case of DES or TripleDES the parity bits are not adjusted. This happens implicitly in
SecretKeyFactory#generateSecret().Additional issues: Apart from key derivation, the CBC mode with a zero IV, PKCS#7 padding and the appropriate encodings for plaintext (Cp1252) and ciphertext (hex encoding) are to be used in the Java code. Since the C# code removes the last byte of the ciphertext for unknown reasons, a dummy byte must be added to the end of the ciphertext in the Java code.
Although the C# code can not be changed according to the description, the following notes on vulnerabilities: The key derivation is weak (this applies to both
CryptDeriveKey()and PBKDF1) and should be replaced by a dedicated password-based key derivation like Argon2 or PBKDF2. A static IV (like a zero IV) is also weak. TripleDES is deprecated and slow and should be replaced with a modern algorithm like AES.