I need to encrypt a string in Java and decrypt it with Perl. I must use Crypt::CBC and the Blowfish algorithm (Crypt::Blowfish). I am using version 2.08 of C::CBC and 2.09 of C::Blowfish. For Java, I am using the default SUN JDK 1.4.2 crypto libraries (part of the standard VM).
I am having a very difficult time decrypting the string in Perl. I am not sure if the problem is with the Java, the Perl, or both. So I am presenting both programs below.
For the Java encryption, the IV is set to all 64-bit zero and the padding is PKCS5Padding. Of course, I tried to match this on the Perl side. C::CBC defaults to PKCS5Padding and I set the IV to "\0\0\0\0\0\0\0\0".
The key is 64 random hexadecimal characters (0-9 and A-F). I think the handling of the key in the Java code could be the cause of the problem, but I am not sure. The Java program prints the encrypted string to the screen, I then copy and paste it to the Perl script, so I can try to decrypt it. (That is how I am testing it.) Here is my Perl code:
my $key = "8326554161EB30EFBC6BF34CC3C832E7CF8135C1999603D4022C031FAEE
+D5C40";
my $vector = "\0\0\0\0\0\0\0\0";
my $cipher = Crypt::CBC->new({
'key' => $key,
'iv' => $vector,
'prepend_iv' => 0,
'cipher' => 'Blowfish',
});
my $plaintext = $cipher->decrypt($encrypted);
Here is the Java code:
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.security.Key;
public class CryptoMain {
public static void main(String[] args) throws Exception {
String mode = "Blowfish/CBC/PKCS5Padding";
String algorithm = "Blowfish";
String secretStr = "8326554161EB30EFBC6BF34CC3C832E7CF8135C1999603
+D4022C031FAEED5C40";
byte secret[] = fromString(secretStr);
Cipher encCipher = null;
Cipher decCipher = null;
byte[] encoded = null;
byte[] decoded = null;
encCipher = Cipher.getInstance(mode);
decCipher = Cipher.getInstance(mode);
Key key = new SecretKeySpec(secret, algorithm);
byte[] ivBytes = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00 };
IvParameterSpec iv = new IvParameterSpec(ivBytes);
encCipher.init(Cipher.ENCRYPT_MODE, key, iv);
decCipher.init(Cipher.DECRYPT_MODE, key, iv);
encoded = encCipher.doFinal(new byte[] {1, 2, 3, 4, 5});
// THIS IS THE ENCODED STRING I USE IN THE PERL SCRIPT
System.out.println("encoded: " + toString(encoded));
decoded = decCipher.doFinal(encoded);
System.out.println("decoded: " + toString(decoded));
encoded = encCipher.doFinal(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20});
System.out.println("encoded: " + toString(encoded));
decoded = decCipher.doFinal(encoded);
System.out.println("decoded: " + toString(decoded));
}
///////// some hex utilities below....
private static final char[] hexDigits = {
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
};
/**
* Returns a string of hexadecimal digits from a byte array. Each
* byte is converted to 2 hex symbols.
* <p>
* If offset and length are omitted, the complete array is used.
*/
public static String toString(byte[] ba, int offset, int length) {
char[] buf = new char[length * 2];
int j = 0;
int k;
for (int i = offset; i < offset + length; i++) {
k = ba[i];
buf[j++] = hexDigits[(k >>> 4) & 0x0F];
buf[j++] = hexDigits[ k & 0x0F];
}
return new String(buf);
}
public static String toString(byte[] ba) {
return toString(ba, 0, ba.length);
}
/**
* Returns the number from 0 to 15 corresponding to the hex digit <i
+>ch</i>.
*/
private static int fromDigit(char ch) {
if (ch >= '0' && ch <= '9')
return ch - '0';
if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
throw new IllegalArgumentException("invalid hex digit '" + ch + "'
+");
}