I am generating a key and need to store it in DB, so I convert it into a String, but to get back the key from the String. What are the possible ways of accomplishing this?
My code is,
SecretKey key = KeyGenerator.getInstance("AES").generateKey();
String stringKey=key.toString();
System.out.println(stringKey);
How can I get the key back from the String?
You can convert the SecretKey to a byte array (byte[]), then Base64 encode that to a String. To convert back to a SecretKey, Base64 decode the String and use it in a SecretKeySpec to rebuild your original SecretKey.
For Java 8
SecretKey to String:
// create new key
SecretKey secretKey = KeyGenerator.getInstance("AES").generateKey();
// get base64 encoded version of the key
String encodedKey = Base64.getEncoder().encodeToString(secretKey.getEncoded());
String to SecretKey:
// decode the base64 encoded string
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
// rebuild key using SecretKeySpec
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
For Java 7 and before (including Android):
NOTE I: you can skip the Base64 encoding/decoding part and just store the byte[] in SQLite. That said, performing Base64 encoding/decoding is not an expensive operation and you can store strings in almost any DB without issues.
NOTE II: Earlier Java versions do not include a Base64 in one of the java.lang or java.util packages. It is however possible to use codecs from Apache Commons Codec, Bouncy Castle or Guava.
SecretKey to String:
// CREATE NEW KEY
// GET ENCODED VERSION OF KEY (THIS CAN BE STORED IN A DB)
SecretKey secretKey;
String stringKey;
try {secretKey = KeyGenerator.getInstance("AES").generateKey();}
catch (NoSuchAlgorithmException e) {/* LOG YOUR EXCEPTION */}
if (secretKey != null) {stringKey = Base64.encodeToString(secretKey.getEncoded(), Base64.DEFAULT)}
String to SecretKey:
// DECODE YOUR BASE64 STRING
// REBUILD KEY USING SecretKeySpec
byte[] encodedKey = Base64.decode(stringKey, Base64.DEFAULT);
SecretKey originalKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
To show how much fun it is to create some functions that are fail fast I've written the following 3 functions.
One creates an AES key, one encodes it and one decodes it back. These three methods can be used with Java 8 (without dependence of internal classes or outside dependencies):
public static SecretKey generateAESKey(int keysize)
throws InvalidParameterException {
try {
if (Cipher.getMaxAllowedKeyLength("AES") < keysize) {
// this may be an issue if unlimited crypto is not installed
throw new InvalidParameterException("Key size of " + keysize
+ " not supported in this runtime");
}
final KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(keysize);
return keyGen.generateKey();
} catch (final NoSuchAlgorithmException e) {
// AES functionality is a requirement for any Java SE runtime
throw new IllegalStateException(
"AES should always be present in a Java SE runtime", e);
}
}
public static SecretKey decodeBase64ToAESKey(final String encodedKey)
throws IllegalArgumentException {
try {
// throws IllegalArgumentException - if src is not in valid Base64
// scheme
final byte[] keyData = Base64.getDecoder().decode(encodedKey);
final int keysize = keyData.length * Byte.SIZE;
// this should be checked by a SecretKeyFactory, but that doesn't exist for AES
switch (keysize) {
case 128:
case 192:
case 256:
break;
default:
throw new IllegalArgumentException("Invalid key size for AES: " + keysize);
}
if (Cipher.getMaxAllowedKeyLength("AES") < keysize) {
// this may be an issue if unlimited crypto is not installed
throw new IllegalArgumentException("Key size of " + keysize
+ " not supported in this runtime");
}
// throws IllegalArgumentException - if key is empty
final SecretKeySpec aesKey = new SecretKeySpec(keyData, "AES");
return aesKey;
} catch (final NoSuchAlgorithmException e) {
// AES functionality is a requirement for any Java SE runtime
throw new IllegalStateException(
"AES should always be present in a Java SE runtime", e);
}
}
public static String encodeAESKeyToBase64(final SecretKey aesKey)
throws IllegalArgumentException {
if (!aesKey.getAlgorithm().equalsIgnoreCase("AES")) {
throw new IllegalArgumentException("Not an AES key");
}
final byte[] keyData = aesKey.getEncoded();
final String encodedKey = Base64.getEncoder().encodeToString(keyData);
return encodedKey;
}
Actually what Luis proposed did not work for me. I had to figure out another way. This is what helped me. Might help you too.
Links:
*.getEncoded(): https://docs.oracle.com/javase/7/docs/api/java/security/Key.html
Encoder information: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Encoder.html
Decoder information: https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Decoder.html
Code snippets:
For encoding:
String temp = new String(Base64.getEncoder().encode(key.getEncoded()));
For decoding:
byte[] encodedKey = Base64.getDecoder().decode(temp);
SecretKey originalKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "DES");
You don't want to use .toString().
Notice that SecretKey inherits from java.security.Key, which itself inherits from Serializable. So the key here (no pun intended) is to serialize the key into a ByteArrayOutputStream, get the byte[] array and store it into the db. The reverse process would be to get the byte[] array off the db, create a ByteArrayInputStream offf the byte[] array, and deserialize the SecretKey off it...
... or even simpler, just use the .getEncoded() method inherited from java.security.Key (which is a parent interface of SecretKey). This method returns the encoded byte[] array off Key/SecretKey, which you can store or retrieve from the database.
This is all assuming your SecretKey implementation supports encoding. Otherwise, getEncoded() will return null.
edit:
You should look at the Key/SecretKey javadocs (available right at the start of a google page):
http://download.oracle.com/javase/6/docs/api/java/security/Key.html
Or this from CodeRanch (also found with the same google search):
http://www.coderanch.com/t/429127/java/java/Convertion-between-SecretKey-String-or
try this, it's work without Base64 ( that is included only in JDK 1.8 ), this code run also in the previous java version :)
private static String SK = "Secret Key in HEX";
// To Encrupt
public static String encrypt( String Message ) throws Exception{
byte[] KeyByte = hexStringToByteArray( SK);
SecretKey k = new SecretKeySpec(KeyByte, 0, KeyByte.length, "DES");
Cipher c = Cipher.getInstance("DES","SunJCE");
c.init(1, k);
byte mes_encrypted[] = cipher.doFinal(Message.getBytes());
String MessageEncrypted = byteArrayToHexString(mes_encrypted);
return MessageEncrypted;
}
// To Decrypt
public static String decrypt( String MessageEncrypted )throws Exception{
byte[] KeyByte = hexStringToByteArray( SK );
SecretKey k = new SecretKeySpec(KeyByte, 0, KeyByte.length, "DES");
Cipher dcr = Cipher.getInstance("DES","SunJCE");
dc.init(Cipher.DECRYPT_MODE, k);
byte[] MesByte = hexStringToByteArray( MessageEncrypted );
byte mes_decrypted[] = dcipher.doFinal( MesByte );
String MessageDecrypeted = new String(mes_decrypted);
return MessageDecrypeted;
}
public static String byteArrayToHexString(byte bytes[]){
StringBuffer hexDump = new StringBuffer();
for(int i = 0; i < bytes.length; i++){
if(bytes[i] < 0)
{
hexDump.append(getDoubleHexValue(Integer.toHexString(256 - Math.abs(bytes[i]))).toUpperCase());
}else
{
hexDump.append(getDoubleHexValue(Integer.toHexString(bytes[i])).toUpperCase());
}
return hexDump.toString();
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2)
{
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
}
return data;
}
Converting SecretKeySpec to String and vice-versa:
you can use getEncoded() method in SecretKeySpec which will give byteArray, from that you can use encodeToString() to get string value of SecretKeySpec in Base64 object.
While converting SecretKeySpec to String: use decode() in Base64 will give byteArray, from that you can create instance for SecretKeySpec with the params as the byteArray to reproduce your SecretKeySpec.
String mAesKey_string;
SecretKeySpec mAesKey= new SecretKeySpec(secretKey.getEncoded(), "AES");
//SecretKeySpec to String
byte[] byteaes=mAesKey.getEncoded();
mAesKey_string=Base64.encodeToString(byteaes,Base64.NO_WRAP);
//String to SecretKeySpec
byte[] aesByte = Base64.decode(mAesKey_string, Base64.NO_WRAP);
mAesKey= new SecretKeySpec(aesByte, "AES");
Related
I was given this .NET code and I need to convert it to Java. I have found a bunch of articles but nothing I try works.
public string EncryptNVal(string vVal, string accountNumber, string sharedSecret)
{
byte[] IV = Convert.FromBase64String(vVal);
byte[] Key = Convert.FromBase64String(sharedSecret);
SymmetricAlgorithm sa = Rijndael.Create();
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, sa.CreateEncryptor(Key, IV),
CryptoStreamMode.Write))
{
byte[] data = Encoding.UTF8.GetBytes(accountNumber);
cs.Write(data, 0, data.Length);
cs.FlushFinalBlock();
ms.Position = 0;
return Convert.ToBase64String(ms.ToArray());
}
}
This is some of my attempt which you can guess throws exceptions.
public String EncryptNVal(String vVal, String accountNumber, String sharedSecret) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
byte[] IV = Base64.decodeBase64(vVal);
byte[] Key =Base64.decodeBase64(sharedSecret);
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
KeySpec keySpec = new PBEKeySpec(Base64.decodeBase64(sharedSecret).toString().toCharArray());
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = factory.generateSecret(keySpec);
PBEParameterSpec paramSpec = new PBEParameterSpec(IV,salt,1000);
Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");
cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
byte[] ciphertext = cipher.doFinal(accountNumber.getBytes(StandardCharsets.UTF_8));
return Base64.encodeBase64String(ciphertext);
In the same project I have this code which I think I got correct, but any confirmation on that would be helpful as well.
.NET code I was given
public string GetMd5Hash(string input)
{
using (var md5Hash = MD5.Create())
{
// Convert the input string to a byte array and compute the hash.
byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
var sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
foreach (var t in data)
{
sBuilder.Append(t.ToString("x2"));
}
// Return the hexadecimal string.
return sBuilder.ToString();
}
}
What I wrote in Java:
public String GetHash(String input) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] inputBytes = input.getBytes("UTF-8");
md.update(inputBytes);
byte inputHash[] = md.digest();
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < inputHash.length; i++) {
hexString.append(Integer.toHexString(0xFF & inputHash[i]));
}
return hexString.toString();
}
On a side note I can use either MD5 or SHA256, the .NET code was using MD5, so I was trying to follow that since my knowledge of encryption is about null. I am willing to use the SHA256 if someone can give me good advice.
I took me some time, and a lot of errors to figure this out, but here's a java method which yields exactly the same output as your C# method.
I'm no expert at all in crypto stuff, but this seems to do the work.
public static String EncryptNVal(String vVal, String accountNumber, String sharedSecret){
try {
byte[] vValAsBytes = java.util.Base64.getDecoder().decode(vVal);
byte[] sharedSecretAsBytes = java.util.Base64.getDecoder().decode(sharedSecret);
byte[] accountNumberAsBytes = accountNumber.getBytes();
SecretKeySpec key = new SecretKeySpec(sharedSecretAsBytes, "AES");
IvParameterSpec iv = new IvParameterSpec(vValAsBytes);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] output = cipher.doFinal(accountNumberAsBytes);
String signature = java.util.Base64.getEncoder().encodeToString(output);
return signature;
}
catch(Exception e){
e.printStackTrace();
return "<dummy>";
}
}
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
A few additional info :
Rijndael is not AES, in the sense that it's not implementing the actual AES standard. If this is for production use, I'd suggest moving to actual AES (take a look at this link for info).
AES/CBC/PKCS5Padding is the closest match I could find for C#'s Rijndael . Although C# seems to use PKCS7, this doesn't seem available for Java (at least not on my system). PKCS5Padding yields the same result, but it will probably not work in 100% of the cases.
It only required trial and error, no deep knowledge of either C# or Java. You'll probably face similar challenges (or related problems which will be induced by your particular use case), and I'd suggest you apply a similar workflow as mine, when it comes to translating from a language to another :
Use and abuse of printing capabilities, to ensure all steps of both codes have the same state
Arrange the code with the same logical flow on both sides
Thoroughly read the documentation of your source function that you want to translate
From the php encryption function below:
$data = "1212312121447";
$cipher = "aes-256-ofb";
$secretKey = "aNdRgUjXn2r5u8x/A?D(G+KbPeShVmYp";
$ivLength = openssl_cipher_iv_length($cipher);
$keyOfb = substr(hash('sha256', $secretKey, true), 0, 32);
$ivOfb = substr($keyOfb, 0, $ivLength);
$encryptedOfb = openssl_encrypt($data, $cipher, $keyOfb, OPENSSL_RAW_DATA, $ivOfb);
echo "ofb-encrypted: " . base64_encode($ivOfb . $encryptedOfb);
the result of encryption is MyFTCJx8RPzOx7h8QNxEtQgeiNIRwnrJ+uc0V70=
And I have try to write this function in Java like this:
public static SecretKeySpec hashKey(String key){
String keyPass = key;
SecretKeySpec result = null;
try{
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(keyPass.getBytes());
byte[] AesKeyData = Arrays.copyOfRange(md.digest(), 0, 32);
SecretKeySpec keySpec = new SecretKeySpec(AesKeyData, "AES");
result = keySpec;
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return result;
}
public static String encryptedOFB(String inp){
String result = "";
String key = "aNdRgUjXn2r5u8x/A?D(G+KbPeShVmYp";
SecretKeySpec keyHashed = hashKey(key);
try{
byte[] initVectorSize = Arrays.copyOfRange(keyHashed.toString().getBytes(), 0, 16);
Cipher cipher = Cipher.getInstance("AES/OFB/NoPadding");
IvParameterSpec iv = new IvParameterSpec(initVectorSize, 0, cipher.getBlockSize());
cipher.init(Cipher.ENCRYPT_MODE, keyHashed, iv);
byte[] encrypted = cipher.doFinal(inp.getBytes());
ByteArrayOutputStream conc = new ByteArrayOutputStream();
conc.write(initVectorSize);
conc.write(encrypted);
byte[] concEnc = conc.toByteArray();
result = new String(Base64.getEncoder().encode(concEnc));
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return result;
}
The result is amF2YXguY3J5cHRvLnNwZYUmrJNv8ycvLua0O9g=
Why my java function return the different result from php?
And how do I fix the java function to get the same result with php?
Thank you.
The IV is determined wrongly. Instead of keyHashed.toString().getBytes() you have to use keyHashed.getEncoded(). Then you get the result of the PHP code.
Apart from that, your key derivation is insecure: since the IV is the first 16 bytes of the key, the same password also means the same key/IV pair, which is insecure. For passwords, it is better to use a reliable key derivation function in conjunction with a randomly generated salt. The IV can be inferred along with the key or randomly generated independently. Salt (or IV) are not secret and can be passed with the ciphertext for decryption, usually concatenated.
When encoding (e.g. inp.getBytes()), the encoding should always be specified (e.g. StandardCharsets.UTF_8). Likewise with the decoding (new String(..., StandardCharsets.UTF_8)). Otherwise the default encoding is used, which can cause cross-platform problems.
I am AES encrypting some text using a randomly generated key and then RSA encrypting that key with a private key so that I can upload it to a database.
The RSA keys are generated using KeyPairGenerator in Java and saved as a file. Keys are read in using File.ReadAllBytes().
When I do this in Java everything works perfectly and the encrypted key is always 172 bytes, but when I do it in C# the encrypted key is always 844 bytes. I'm pretty sure the text is being encrypted properly using AES, but something is going wrong with the RSA encryption.
I have checked the key sizes in Java and C# and they always match. Literally, the only difference I can see is the RSA encrypted ciphertext length, which makes the data unusable. I believe it has something to do with padding, but I don't know how to fix it.
Java
public String encryptText(String msg, PrivateKey key)
throws NoSuchAlgorithmException, NoSuchPaddingException,
UnsupportedEncodingException, IllegalBlockSizeException,
BadPaddingException, InvalidKeyException {
KeyGenerator generator;
this.cipher.init(Cipher.ENCRYPT_MODE, key); //cipher is initialized earlier with this.cipher = Cipher.getInstance("RSA");
try {
generator = KeyGenerator.getInstance(AES);
generator.init(128); // The AES key size in number of bits
SecretKey secKey = generator.generateKey();
Cipher aesCipher = Cipher.getInstance(AES);
aesCipher.init(Cipher.ENCRYPT_MODE, secKey);
String encText = Base64.getEncoder().encodeToString(aesCipher.doFinal(msg.getBytes("UTF-8")));
String encKey = Base64.getEncoder().encodeToString(cipher.doFinal(secKey.getEncoded()));
return "(" + encText + ")" + encKey;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
C#
public String EncryptText(byte[] privateKeyBytes, string msg)
{
try
{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = RSA.ExportParameters(false);
RSAKeyInfo.Modulus = privateKeyBytes;
RSA.ImportParameters(RSAKeyInfo);
RijndaelManaged aes = new RijndaelManaged();
aes.BlockSize = 128;
aes.KeySize = 128;
aes.Mode = CipherMode.ECB;
byte[] keyGenerated = aes.Key;
string keyStr = Convert.ToBase64String(keyGenerated);
byte[] keyArr = Convert.FromBase64String(keyStr);
byte[] KeyArrBytes16Value = new byte[16];
Array.Copy(keyArr, KeyArrBytes16Value, 16);
aes.Key = KeyArrBytes16Value;
ICryptoTransform encrypto = aes.CreateEncryptor();
byte[] plainTextByte = ASCIIEncoding.UTF8.GetBytes(msg);
byte[] CipherText = encrypto.TransformFinalBlock(plainTextByte, 0, plainTextByte.Length);
string encText = Convert.ToBase64String(CipherText);
string encKey = Convert.ToBase64String(RSA.Encrypt(aes.Key, true));
return "(" + encText + ")" + encKey;
}
catch (CryptographicException e)
{
Console.WriteLine("FAILED: " + e.Message);
}
return null;
}
UPDATE
Thanks to Henno for pointing out that the problem was in how I was reading the key. I ended up using Bouncy Castle to handle the RSA encryption in C#. I also changed my java code to encrypt with the public key instead of the private key.
New C#
public String EncryptText(byte[] keyBytes, string msg)
{
try
{
AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(keyBytes);
RsaKeyParameters rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter;
RSAParameters rsaParameters = new RSAParameters();
rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned();
rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned();
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
RijndaelManaged aes = new RijndaelManaged();
aes.BlockSize = 128;
aes.KeySize = 128;
aes.Mode = CipherMode.ECB;
byte[] keyGenerated = aes.Key;
string keyStr = Convert.ToBase64String(keyGenerated);
byte[] keyArr = Convert.FromBase64String(keyStr);
byte[] KeyArrBytes16Value = new byte[16];
Array.Copy(keyArr, KeyArrBytes16Value, 16);
aes.Key = KeyArrBytes16Value;
ICryptoTransform encrypto = aes.CreateEncryptor();
byte[] plainTextByte = ASCIIEncoding.UTF8.GetBytes(msg);
byte[] CipherText = encrypto.TransformFinalBlock(plainTextByte, 0, plainTextByte.Length);
string encText = Convert.ToBase64String(CipherText);
string encKey = Convert.ToBase64String(rsa.Encrypt(aes.Key, false));
return "(" + encText + ")" + encKey;
}
catch (CryptographicException e)
{
Console.WriteLine("FAILED: " + e.Message);
}
return null;
}
What seems to go wrong is that you read in the saved "private key file" in C#, presumably in the variable privateKeyBytes (but your code is incomplete, so I'm guessing) and then do RSAKeyInfo.Modulus = privateKeyBytes, which is weird and cryptographically implausible. You should instantiate some kind of RSA class in C# as well, based on the bytes you read in, which is what I think you're trying to do in the beginning of the C# code (first four lines). I think there should be another API for that, looking around in the docs:
RSA.ImportParameters(RSAKeyInfo) and then maybe set RSAKeyInfo from those bytes, but it's not the modulus. The read in bytes should be PKCS1 format or something similar, maye base64 encoded in file, or raw etc. You'd have to look into what format Java uses to export full keys to disk.
You use the raw bytes you read in from file as a modulus, which is surely going to give trouble and gives a "key" that is invalid and much too big as well.
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.
Why does not this AES encryption work? I've written it in Java to test, but I am not able to decrypt. I get garbage upon decryption. Why? Its so simple - In the main method, print plain text, encrypt, print cipher text, decrypt, print plain text again. Am I doing something wrong? Please help me figure out the problem.
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AESTest {
public static void main(String [] args) {
try {
String plainText = "Hello World!!!!!";
String encryptionKey = "E072EDF9534053A0B6C581C58FBF25CC";
System.out.println("Before encryption - " + plainText);
String cipherText = encrypt(plainText, encryptionKey);
System.out.println("After encryption - " + cipherText);
String decrypted = decrypt(cipherText, encryptionKey);
System.out.println("After decryption - " + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String encrypt(String plainText, String passkey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
String cipherText = new String(cipher.doFinal(plainText.getBytes()));
return cipherText;
}
public static String decrypt(String cipherText, String passkey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
String plainText = new String(cipher.doFinal(cipherText.getBytes()));
return plainText;
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}
The output of the cipher is a sequence of random-looking bytes. You have no guarantee that these bytes will be a valid encoding for a character string in whatever is your system's default encoding. So this line:
String cipherText = new String(cipher.doFinal(.....));
is likely to lose information that you'll need for decryption.
Therefore you will not get the right bytes reconstructed in your decrypt operation. For example, if your default encoding is UTF-8, it is overwhelmingly unlikely that the correct ciphertext is something that String.getBytes() is even able to produce.
Two things:
No padding can only work if you use input that is an exact mulitple of your key size, which is 128 bit or 16 bytes. So in your particular case "Hello World!!!!!".getBytes() is actually a multiple of 16, but this is of course not true for arbitrary Strings.
Use "AES/CBC/PKCS5Padding" instead to solve this issue.
Do not turn your encrypted data into a String - this will and change the encrypted output. There's no guarantee that new String(byte[]).getBytes() returns the exact same byte array!
So you should leave the encrypted data as what it is - a stream of bytes. Thus encrypt should return byte[] instead and decrypt should take byte[] as input - this is a working example:
public class NewClass {
public static void main(String [] args) {
try {
String plainText = "Hello World!!!!";
String encryptionKey = "E072EDF9534053A0B6C581C58FBF25CC";
System.out.println("Before encryption - " + plainText);
byte[] cipherText = encrypt(plainText, encryptionKey);
System.out.println("After encryption - " + cipherText);
String decrypted = decrypt(cipherText, encryptionKey);
// -> Hello World!!!!
System.out.println("After decryption - " + decrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] encrypt(String plainText, String passkey) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
return cipher.doFinal(plainText.getBytes());
}
public static String decrypt(byte[] cipherText, String passkey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(hexStringToByteArray(passkey), "AES");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(new byte[cipher.getBlockSize()]));
return new String(cipher.doFinal(cipherText));
}
You need to create the SecretKeySpec object once and use it for both encrypt and decrypt. Currently the code is creating two different keys for each operation and this will definitely lead to incorrect results.