Crypto interop between .NET & Java
Of late I have been searching for ways to encrypt text in Java and decrypt the same in .NET. I couldn't find anything that met my needs. The closest one I found was in Java forums: http://forum.java.sun.com/thread.jspa?threadID=734949&messageID=4227225
I decided to write a sample that has both encryption and decryption in both Java and .NET. I am no crypto expert. However I did some research and found that the default .NET encryption configuration is set to high. On the Java side, I did not find any out of the box encryption libraries with JDK SE 1.6 that are good enough for .NET. BouncyCastle has a great library for encryption. You can find that at: http://www.bouncycastle.org/. They also do have a port for .NET (C#).
Anyways, here's my take at the sample:
.NET Code
1 /*****************************************************
2 * Author : Charles Prakash Dasari
3 * Copyright © 2007 Charles Prakash Dasari.
4 *
5 * THE AUTHOR DOES NOT ASSUME ANY RESPONSIBILITY FOR ANY KIND OF USAGE OF THE PROVIDED
6 * SOURCE CODE. THE CODE IS PROVIDED AS IS.
7 *
8 * ---------------------------------------------------
9 *
10 * You are free to use and modify this code to suit your needs as long as
11 * you acknowledge the author and waive the author of any responsibility whatsoever.
12 * You must include the above statement as is.
13 *
14 * ****************************************************/
15 16 using System;
17 using System.Collections.Generic;
18 using System.Text;
19 using System.Security.Cryptography;
20 21 namespace crypttest
22 {23 public class CryptFinal
24 {25 static string passPhrase = "abcdefghijklmnopqrstuvwx";
26 static byte[] salt = {
27 (byte)0x49, (byte)0x76, (byte)0x61, (byte)0x6e, (byte)0x20, (byte)0x4d,
28 (byte)0x65, (byte)0x64, (byte)0x76, (byte)0x65, (byte)0x64, (byte)0x65,
29 (byte)0x76
30 };31 static int iterationCount = 1000;
32 static int keyLength = 256; // 32 bytes
33 static int ivLength = 128; // 16 bytes
34 35 public static void Main(string[] args)
36 {37 string password;
38 Console.Write("Password to encrypt: ");
39 password = Console.ReadLine();
40 41 string encrypted = Encrypt(password);
42 string decrypted = Decrypt(encrypted);
43 44 Console.WriteLine("Encrypted: " + encrypted);
45 Console.WriteLine("Decrypted: " + decrypted);
46 } 47 48 static ICryptoTransform ConstructCipher(bool forEncryption)
49 {50 byte[] passPhraseBytes = ToUtf8ByteArray(passPhrase);
51 Rfc2898DeriveBytes paramGen = new Rfc2898DeriveBytes(
52 passPhraseBytes, 53 salt, 54 iterationCount 55 );56 Rijndael algorithm = Rijndael.Create();
57 byte[] key = paramGen.GetBytes(keyLength / 8);
58 byte[] iv = paramGen.GetBytes(ivLength / 8);
59 60 return (forEncryption ? algorithm.CreateEncryptor(key, iv)
61 : algorithm.CreateDecryptor(key, iv)); 62 }63 static byte[] ProcessBytesWithCipher(ICryptoTransform cipher, byte[] input)
64 {65 return cipher.TransformFinalBlock(input, 0, input.Length);
66 }67 static string Encrypt(string input)
68 {69 return ToBase64String(
70 ProcessBytesWithCipher(71 ConstructCipher(true),
72 ToUtf8ByteArray(input) 73 ) 74 ); 75 }76 static string Decrypt(string input)
77 {78 return FromUtf8ByteArray(
79 ProcessBytesWithCipher(80 ConstructCipher(false),
81 FromBase64String(input) 82 ) 83 ); 84 }85 static string ToBase64String(byte[] input)
86 {87 return Convert.ToBase64String(input);
88 }89 static byte[] FromBase64String(string input)
90 {91 return Convert.FromBase64String(input);
92 }93 static byte[] ToUtf8ByteArray(string input)
94 {95 return Encoding.UTF8.GetBytes(input);
96 }97 static string FromUtf8ByteArray(byte[] input)
98 {99 return Encoding.UTF8.GetString(input);
100 } 101 } 102 }
Java Code
1 /**
2 * Author : Charles Prakash Dasari
3 * Copyright © 2007 Charles Prakash Dasari.
4 *
5 * THE AUTHOR DOES NOT ASSUME ANY RESPONSIBILITY FOR ANY KIND OF USAGE OF THE PROVIDED
6 * SOURCE CODE. THE CODE IS PROVIDED AS IS.
7 *
8 * ---------------------------------------------------
9 *
10 * You are free to use and modify this code to suit your needs as long as
11 * you acknowledge the author and waive the author of any responsibility whatsoever.
12 * You must include the above statement as is.
13 *
14 */
15 16 import java.io.BufferedReader;
17 import java.io.IOException;
18 import java.io.InputStreamReader;
19 import java.io.UnsupportedEncodingException;
20 21 import org.bouncycastle.crypto.BufferedBlockCipher;
22 import org.bouncycastle.crypto.CipherParameters;
23 import org.bouncycastle.crypto.engines.AESEngine;
24 import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
25 import org.bouncycastle.crypto.modes.CBCBlockCipher;
26 import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
27 28 import sun.misc.BASE64Decoder;
29 import sun.misc.BASE64Encoder;
30 31 /**
32 * @author prakashd
33 *
34 */
35 public class CryptFinal {
36 37 static String passPhrase = "abcdefghijklmnopqrstuvwx";
38 static byte[] salt = {
39 (byte)0x49, (byte)0x76, (byte)0x61, (byte)0x6e, (byte)0x20, (byte)0x4d,
40 (byte)0x65, (byte)0x64, (byte)0x76, (byte)0x65, (byte)0x64, (byte)0x65,
41 (byte)0x76
42 };43 static int iterationCount = 1000;
44 static int keyLength = 256; // 32 bytes
45 static int ivLength = 128; // 16 bytes
46 47 /**
48 * @param args
49 */
50 public static void main(String[] args) {
51 try {
52 String password = "";
53 54 System.out.print("Password to encrypt: ");
55 BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
56 password = reader.readLine(); 57 58 String encrypted = encrypt(password);
59 String decrypted = decrypt(encrypted);
60 61 System.out.println("Encrypted: " + encrypted);
62 System.out.println("Decrypted: " + decrypted);
63 64 } catch (Throwable e) {
65 e.printStackTrace(); 66 } 67 }68 static BufferedBlockCipher constructCipher(boolean forEncryption) {
69 byte[] passPhraseBytes = toUtf8ByteArray(passPhrase);
70 71 PKCS5S2ParametersGenerator paramGen = new PKCS5S2ParametersGenerator();
72 paramGen.init(passPhraseBytes, salt, iterationCount); 73 CipherParameters cipherParam = paramGen.generateDerivedParameters( 74 keyLength, 75 ivLength 76 ); 77 78 BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
79 new CBCBlockCipher(new AESEngine())
80 ); 81 82 cipher.init(forEncryption, cipherParam); 83 84 return cipher;
85 }86 static byte[] processBytesWithCipher(BufferedBlockCipher cipher, byte[] input) {
87 try {
88 int expectedLength = cipher.getOutputSize(input.length);
89 90 byte[] processed = new byte[expectedLength];
91 92 int length = cipher.processBytes(input, 0, input.length, processed, 0);
93 length += cipher.doFinal(processed, length); 94 95 byte[] output = new byte[length];
96 97 for (int i = 0; i < length; i++) output[i] = processed[i];
98 return output;
99 } catch (Throwable e) {
100 // TODO: handle exception
101 e.printStackTrace(); 102 } 103 104 return null;
105 }106 static String encrypt(String input) {
107 return toBase64String(
108 processBytesWithCipher(109 constructCipher(true),
110 toUtf8ByteArray(input) 111 ) 112 ); 113 }114 static String decrypt(String input) {
115 return fromUtf8ByteArray(
116 processBytesWithCipher(117 constructCipher(false),
118 fromBase64String(input) 119 ) 120 ); 121 }122 static String toBase64String(byte[] input) {
123 // TODO: Since the below class is deprecated, find an alternate
124 // for Base64 encoding
125 return new BASE64Encoder().encode(input);
126 }127 static byte[] fromBase64String(String input) {
128 // TODO: Since the below class is deprecated, find an alternate
129 // for Base64 encoding
130 try {
131 return new BASE64Decoder().decodeBuffer(input);
132 } catch (IOException e) {
133 // TODO Auto-generated catch block
134 e.printStackTrace(); 135 } 136 137 return null;
138 }139 static byte[] toUtf8ByteArray(String input) {
140 try {
141 return input.getBytes("UTF8");
142 } catch (UnsupportedEncodingException e) {
143 // TODO Auto-generated catch block
144 e.printStackTrace(); 145 } 146 147 return null;
148 }149 static String fromUtf8ByteArray(byte[] input) {
150 try {
151 return new String(input, "UTF8");
152 } catch (UnsupportedEncodingException e) {
153 // TODO Auto-generated catch block
154 e.printStackTrace(); 155 } 156 157 return null;
158 } 159 }Notes:
- This sample code fails to comply with the security best practices for encryption – like having the password phrase in the code, and probably more. When you use this code, you have to incorporate into your system adhering to the security practices that are followed within that system.
- There is a known issue. If the input is longer than 47 characters, we see extra carriage return and line feed characters (\r\n) in the encoded output. My suspicion is that the base-64 encoder is buggy. The best way to identify that is to compare the produced bytes both in Java and in .NET before they are base-64 encoded. I didn't get the time to do the same. Any confirmation on the same will be appreciated.
- This sample uses the 256-bit encryption using Rijndael algorithm (aka AES in BouncyCastle terminology).
- This sample uses Java's deprecated sun.misc.BASE64Encoder/Decoder classes. You have to use your own base-64 encoding helpers.
Enjoy!
0 Comments:
Post a Comment
Subscribe to Post Comments [Atom]
<< Home