I'm having an issue trying to encrypt and decrypt a string using BouncyCastle.
I'm following an example at http://www.aviransplace.com/2004/10/12/using-rsa-encryption-with-java/ and my code looks like:
public class Cryptotests {
public static final String ALGORITHM = "RSA";
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
try {
init();
KeyPair kp = generateKey();
byte[] enc = encrypt("The Fat Cat Jumped Over the Bat".getBytes("UTF8"), kp.getPublic());
byte[] dec = decrypt(enc, kp.getPrivate());
} catch (Exception ex) {
Logger.getLogger(Cryptotests.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void init() {
Security.addProvider(new BouncyCastleProvider());
}
public static KeyPair generateKey() throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
keyGen.initialize(1024);
KeyPair key = keyGen.generateKeyPair();
return key;
}
/**
* Encrypt a text using public key.
*
* #param text The original unencrypted text
* #param key The public key
* #return Encrypted text
* #throws java.lang.Exception
*/
public static byte[] encrypt(byte[] text, PublicKey key) throws Exception {
byte[] cipherText = null;
// get an RSA cipher object and print the provider
Cipher cipher = Cipher.getInstance(
"RSA / ECB / PKCS1Padding");
System.out.println(
"nProvider is:" + cipher.getProvider().getInfo());
// encrypt the plaintext using the public key
cipher.init(Cipher.ENCRYPT_MODE, key);
cipherText = cipher.doFinal(text);
return cipherText;
}
/**
* Decrypt text using private key
*
* #param text The encrypted text
* #param key The private key
* #return The unencrypted text
* #throws java.lang.Exception
*/
public static byte[] decrypt(byte[] text, PrivateKey key) throws Exception {
byte[] dectyptedText = null;
// decrypt the text using the private key
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
dectyptedText = cipher.doFinal(text);
return dectyptedText;
}
}
When I Run this code I end up with an error:
May 21, 2013 10:20:31 AM cryptotests.Cryptotests main
SEVERE: null
java.security.InvalidKeyException: Illegal key size or default parameters
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1011)
at javax.crypto.Cipher.init(Cipher.java:1209)
at javax.crypto.Cipher.init(Cipher.java:1153)
at cryptotests.Cryptotests.encrypt(Cryptotests.java:70)
at cryptotests.Cryptotests.main(Cryptotests.java:34)
I'm really new and quite honestly feeling a bit lost when it comes to cryptography. My goal is to figure this out so that I can create and use a RSA key pair using SHA512 and a 4k length. I'm having a lot of trouble finding clear examples of how to do achieve this.
Need to install the Unlimited Java Cryptography Extension (JCE) Unlimited Strength
http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
Related
I am trying to replicate the following PHP class in Java to decrypt a string that was encrypted using openssl_encrypt in PHP.
class Encryption {
/* Encryption method */
protected $method = 'aes-128-ctr';
/* Encryption key */
private $key;
/* Property constructor */
public function __construct($key = FALSE, $method = FALSE){
/* Check if custom key provided */
if(!$key) {
/* Set default encryption if none provided */
$key = '1234567891234567';
}
/* Check for control characters in key */
if(ctype_print($key)) {
/* Convert ASCII keys to binary format */
$this->key = openssl_digest($key, 'SHA256', true);
} else {
$this->key = $key;
}
$this->key = $key;
/* Check for custom method */
if($method) {
/* If it is a valid openssl cipher method, use it */
if(in_array(strtolower($method), openssl_get_cipher_methods())) {
$this->method = $method;
/* If it is not, unrecognised method */
} else {
die(__METHOD__ . ": unrecognised cipher method: {$method}");
}
}
}
/* Get iv bytes length */
protected function iv_bytes(){
/* Get length of encryption cipher initialisation vector */
return openssl_cipher_iv_length($this->method);
}
/* Encryption method */
public function encrypt($data) {
/* Get initialisation vector binary */
$iv = openssl_random_pseudo_bytes($this->iv_bytes());
/* Return IV hex & encryption string */
return bin2hex($iv) . openssl_encrypt($data, $this->method, $this->key, OPENSSL_ZERO_PADDING, $iv);
}
/* Decrypt encrypted string */
public function decrypt($data){
/* Get IV string length */
$iv_strlen = 2 * $this->iv_bytes();
/* Parse out the encryption string and unpack the IV and encrypted string. $regs is passed by reference in preg_match() */
if(preg_match("/^(.{" . $iv_strlen . "})(.+)$/", $data, $regs)) {
list(, $iv, $crypted_string) = $regs;
print_R($regs);
/* If there are character representing a hex and the IV string length is divisible by 2 */
if(ctype_xdigit($iv) && (strlen($iv) % 2 == 0)) {
/* Decrypt the unpacked encrypted string */
return openssl_decrypt($crypted_string, $this->method, $this->key, OPENSSL_ZERO_PADDING, hex2bin($iv));
}
}
return FALSE; // failed to decrypt
}
}
Here is my current sandbox class for the decryptor in Java
public class PHPDecryptor {
private static String removeIVHash(String data) {
/* IV string length is 128 bit (16 bytes) * 2 */
int iv_strlen = 2 * 16;
/* Remove IV hash */
String encrypted_string = data.substring(iv_strlen);
return encrypted_string;
}
public static String decrypt(String data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String encrypted_string = removeIVHash(data);
/* Get bytes for string and key */
byte[] text_to_decrypt = encrypted_string.getBytes(StandardCharsets.UTF_8);
String key_string = "1234567891234567";
byte[] key = key_string.getBytes(StandardCharsets.UTF_8);
/* Get secret key and iv */
SecretKeySpec secret_key = new SecretKeySpec(key, "AES");
IvParameterSpec iv = new IvParameterSpec(new byte[16]);
/* Init cipher */
Cipher cipher = Cipher.getInstance("AES/CTR/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret_key, iv);
/* Decrypt and cast to string */
byte[] decrypted = cipher.doFinal(text_to_decrypt);
String result = new String(decrypted, StandardCharsets.UTF_8);
return result;
}
public static void main(String[] arg) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
String text_to_decrypt = "203790144a345320d98fb773795d518e/ioQTApeVMV/4g=="; // Encrypted by PHP
System.out.println(decrypt(text_to_decrypt));
}
}
However, I am not getting an accurate decryption - in fact the return result from the Java class is utter nonsense with invalid UTF-8 characters.
Are there any obvious errors here?
It seems like you're not doing anything with the IV, just removing it.
Create an instance of IVParameterSpec from the IV that you removed.
public static IvParameterSpec getIv(String iv) {
return new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
}
You can retrieve the IV from the string better if you use some delimiter (I like to use ":") to separate the iv and the rest of the ciphertext, that way you don't need to worry about the hexed IV length and retrieve it like this:
private static String decodeIvString(String data) {
String[] parts = data.split(":");
String iv = decodeHex(parts[0]);
return iv;
}
Obviously you'll have to add the delimiter in the encrypt function in php.
You can then pass the IV back to the getIv() function.
/* Encryption method */
public function encrypt($data) {
/* Get initialisation vector binary */
$iv = openssl_random_pseudo_bytes($this->iv_bytes());
/* Return IV hex & encryption string */
return bin2hex($iv) . ":" . openssl_encrypt($data, $this->method, $this->key, OPENSSL_ZERO_PADDING, $iv);
}
And as the comments above mentioned, you should replace PKCS5Padding with NoPadding
Here's a utility piece of code that I use to decrypt:
public static byte[] decryptSymmetric(Algorithm algorithm, Key key, IvParameterSpec iv, byte[] data) throws CryptoFailure {
try {
Security.addProvider(new BouncyCastleProvider());
Cipher aes = Cipher.getInstance(algorithm.getMode(), BouncyCastleProvider.PROVIDER_NAME);
aes.init(Cipher.DECRYPT_MODE, key, iv);
return aes.doFinal(data);
} catch (BadPaddingException | IllegalBlockSizeException e) {
throw new CryptoFailure(e);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
throw new CryptoError(e);
}
}
I am trying to change JAVA Encryption to PHP and produce exactly the same result.
I have the following guidelines.
AES ‐ CBC with PKCS5 Padding Symmetric Encryption Scheme:
Encryption key size would be of 128 bit size.
Initialization Vector (IV) :
-New Random IV would be used in each request.
-In a single web‐service request, same IV would be used while encrypting all the encrypted fields.
-This IV would be passed in SOAP Header with name as “IV”. IV value would be Base64 encoded.
I have tried this https://gist.github.com/thomasdarimont/fae409eaae2abcf83bd6633b961e7f00
public class AESEncryptionUtil {
public static final String CLASS_NAME = AESEncryptionUtil.class.getName(); private static final int KEY_SIZE = 16;
private static final String ALGORITHM_AES = "AES";
public final static String ALGORITHM_AES_CBC = "AES/CBC/PKCS5Padding";
private static Key generateKey(String keyValue) throws Exception { Key key = null ;
if (keyValue!=null && keyValue.length()==KEY_SIZE){
byte[] byteKey = keyValue.substring(0, KEY_SIZE).getBytes("UTF-8");
key = new SecretKeySpec(byteKey, ALGORITHM_AES);
}else{
System.out.println("Not generating the Key!! "+keyValue); }
return key;
}
/**
* Return Base64 Encoded value of IV *
* #param keyValue * #return
* #throws Exception */
public static String generateIV(String keyValue) throws Exception { String iv = null ;
Key key = generateKey(keyValue); if (key!=null){
Cipher cipher = Cipher.getInstance(ALGORITHM_AES_CBC); cipher.init(Cipher.ENCRYPT_MODE, key); AlgorithmParameters params = cipher.getParameters();
iv = new BASE64Encoder().encode(params.getParameterSpec(IvParameterSpec.class).getIV());
}else{
System.out.println("No IV generated ...");
}
return iv; }
/**
* Method to perform encryption of given data with AES Algorithm / Key and IV. * #param encKey -
*Encryption Key value * #param plainVal -
*Value to be encrypted * #return - encrypted String Value * #throws Exception
*/
public static String encrypt(String encKey, String plainVal, String currentIV) throws Exception {
String encryptedText = null ; Key key = generateKey(encKey);
if (key!=null && currentIV!=null && plainVal!=null){
Cipher c = Cipher.getInstance(ALGORITHM_AES_CBC);
c.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new BASE64Decoder().decodeBuffer(currentIV)));
byte[] encValue = c.doFinal(plainVal.getBytes()); encryptedText= new BASE64Encoder().encode(encValue);
}else{
System.out.println("Invalid input passed to encrypt !! keyValue="+encKey+", IV="+currentIV+", valueToEnc="+plainVal);
}
return encryptedText; }
}
I managed to get something to work
<?php
$string = "online1234";
$key = "haskingvista127$";
$iv = base64_encode(openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-128-cbc')));
$encodedEncryptedData = base64_encode(openssl_encrypt($string, "AES-128-CBC", $key, OPENSSL_RAW_DATA, base64_decode($iv)));
$decryptedData = openssl_decrypt(base64_decode($encodedEncryptedData), "AES-128-CBC", $key, OPENSSL_RAW_DATA, base64_decode($iv));
?>
Hope this might help someone else
I am trying to decrypt using initialization vector and key at client side, but the GWT is not able to recognize it, I added crypto library but still it is not supported. How can I use initialization vector to make encryption and decryption more secure.
At server side i am able to encrypt but at client side i am not able to decrypt..
KeyGenerator and IvParameterSpec is not supported by GWT
private String encryptDES(String sessionKey) throws Exception {
KeyGenerator keygenerator = KeyGenerator.getInstance("DESede");
SecretKey myKey = keygenerator.generateKey();
SecureRandom sr = new SecureRandom();
byte [] iv = new byte[8];
sr.nextBytes(iv);
IvParameterSpec IV = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, myKey, IV);
String encrypted = Base64.encode(cipher.doFinal(sessionKey.getBytes()));
return encrypted;
}
please help me resolving it
Write a wrapper around cryptoJS
Should be less than 100 lines of code.
Inject for example aes from crypto.js
String url = GWT.getModuleBaseForStaticFiles() + "js/aes.js";
ScriptInjector.fromUrl(url).setWindow(ScriptInjector.TOP_WINDOW).inject();
Encrypt
/**
* Encrypt the given String with the given key.
*
* #param s The String to encrypt
* #param cipher The cipher
* #return The encrypted String
*/
public static native String encrypt(String s, String cipher)
/*-{
var key = $wnd.CryptoJS.enc.Utf8.parse(cipher);
var iv = $wnd.CryptoJS.enc.Utf8.parse(cipher);
var encrypted = $wnd.CryptoJS.AES.encrypt($wnd.CryptoJS.enc.Utf8.parse(s), key,
{
keySize: 128 / 8,
iv: iv,
mode: $wnd.CryptoJS.mode.CBC,
padding: $wnd.CryptoJS.pad.Pkcs7
});
return encrypted;
}-*/;
Decrypt
/**
* Decrypt the given String with the given key.
*
* #param s The String to decrypt
* #param cipher The key
* #return The decrypted String
*/
public static native String decrypt(String s, String cipher)
/*-{
var key = $wnd.CryptoJS.enc.Utf8.parse(cipher);
var iv = $wnd.CryptoJS.enc.Utf8.parse(cipher);
var decrypted = $wnd.CryptoJS.AES.decrypt(s, key,
{
keySize: 128 / 8,
iv: iv,
mode: $wnd.CryptoJS.mode.CBC,
padding: $wnd.CryptoJS.pad.Pkcs7
});
return decrypted.toString($wnd.CryptoJS.enc.Utf8);
}-*/;
I'm trying to sign an encrypted message with a private key and verify it in Java. This is my first time working with encryption and signatures so I'm not sure how it is supposed to work and I'm kind of stuck here. The verification always returns false.
Here I sign the message:
public byte[] rsaSign (byte[] data) {
byte[] cipherData = null;
try {
RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(signModulus, signExponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey privKey = fact.generatePrivate(keySpec);
Signature s = Signature.getInstance("SHA1withRSA");
s.initSign(privKey);
s.update(data);
return s.sign();
}
return cipherData;
}
And here I try to verify the signature:
public boolean rsaVerify (byte[] data, byte[] signature) {
boolean success = false;
try {
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(signModulus, signPublicExponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(keySpec);
Signature s = Signature.getInstance("SHA1withRSA");
s.initVerify(pubKey);
s.update(data);
success = s.verify(signature);
return success;
}
return false;
}
Can anyone see a problem? The keys are generated in C# and converted to BigIntegers in java.
Signature verification is failed because you are using a different public key in the verification method.
Use the public key to verify the signature which is consistent with the private key that is used into rsaSign() method.
Hope this will help you. Note that, this public key is consistent with the private key which is used in Signature Generation method :
/**
* This method will sign message with RSA 2048 key
* #return Void
*/
public void rsaSign (String message) throws Exception {
//key generation
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
keyGen.initialize(2048, random);
KeyPair keyPair = keyGen.generateKeyPair();
PrivateKey priv = keyPair.getPrivate();
PublicKey pub = keyPair.getPublic();
System.out.println("RSAPub key Mod for Sign/Verify : " + Helper.toHex(((RSAPublicKey)pub).getModulus().toByteArray()));
System.out.println("RSAPub key Exp for Sign/Verify : " + Helper.toHex(((RSAPublicKey)pub).getPublicExponent().toByteArray()));
//sign
Signature dsa = Signature.getInstance(signALG);
dsa.initSign(priv);
dsa.update(Helper.toByte(message));
byte[] realSig = dsa.sign();
System.out.println("RSA Sign-Data : " + Helper.toHex(realSig));
}
/**
* This method verify signature with RSA public key
* #param message The plain message
* #param rsaMOD RSA Public key Modulus in string
* #param rsaEXP RSA Public key Exponent in string
* #param rsaSignData Signature which will be verified
* #return true if verifications success, false otherwise
*/
public boolean rsaVerify(String message, String rsaMOD, String rsaEXP, String rsaSignData) throws Exception {
BigInteger modBigInteger = new BigInteger(Helper.toByte(rsaMOD));
BigInteger exBigInteger = new BigInteger(Helper.toByte(rsaEXP));
RSAPublicKeySpec spec = new RSAPublicKeySpec(modBigInteger, exBigInteger);
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey publicKey = factory.generatePublic(spec);
Signature signature = Signature.getInstance(signALG);
signature.initVerify(publicKey);
signature.update(Helper.toByte(message));
return signature.verify(Helper.toByte(rsaSignData));
}
You should try and test these things locally first, with your own generated key pair. If that fails your code is wrong - it's a very simple wrapper around Java Signature so that's not at all that likely.
You already used a complete specification of the signature algorithm, so provider defaults are not an issue here.
Then check the correctness of the data on both sides by printing it out in Hex or Base64 right before signature generation/verification. If that fails you've got an I/O or encoding/decoding error. Encoding/decoding errors & string handling make up about 30% of the total of cryptography related questions!
Finally you could obtain and compare the modulus of the private and public key. If the moduli don't match then you're using a private and public key of a different key pair, and signature verification will of course always fail.
I have the following class I use to store encrypted preferences to use with my application (using interface with 3rd part site which does not support OAuth)...
public class CryptoTranslator {
private static SecretKey SEC_KEY;
/**
* #return the sEC_KEY
*/
public static SecretKey getSEC_KEY() {
return SEC_KEY;
}
public static String getSEC_KEY_String(){
return Base64.encodeToString(SEC_KEY.getEncoded(), Base64.DEFAULT);
}
/**
* #param sEC_KEY the sEC_KEY to set
*/
public static void setSEC_KEY(SecretKey sEC_KEY) {
SEC_KEY = sEC_KEY;
}
public static void setSEC_KEY_STRING(String sEC_KEY){
byte[] key = Base64.decode(sEC_KEY, Base64.DEFAULT);
SEC_KEY = new SecretKeySpec(key, 0, key.length, "AES");
}
public static void generateKey() throws NoSuchAlgorithmException {
// Generate a 256-bit key
final int outputKeyLength = 256;
SecureRandom secureRandom = new SecureRandom();
// Do *not* seed secureRandom! Automatically seeded from system entropy.
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(outputKeyLength, secureRandom);
SecretKey key = keyGenerator.generateKey();
SEC_KEY = key;
}
private static byte[] getRawKey() throws Exception {
if (SEC_KEY == null){
generateKey();
}
byte[] raw = SEC_KEY.getEncoded();
return raw;
}
/**
*
*
* #param clear clear text string
* #param mode this should either be Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE
* #return
* #throws Exception
*/
private static String translate(String clear, int mode) throws Exception {
if(mode != Cipher.ENCRYPT_MODE && mode != Cipher.DECRYPT_MODE)
throw new IllegalArgumentException("Encryption invalid. Mode should be either Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE");
SecretKeySpec skeySpec = new SecretKeySpec(getRawKey(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(mode, skeySpec);
byte[] encrypted = cipher.doFinal(clear.getBytes());
return new String(encrypted);
}
public static String encrypt(String clear) throws Exception {
return translate(clear,Cipher.ENCRYPT_MODE);
}
public static String decrypt(String encrypted) throws Exception {
return translate(encrypted,Cipher.DECRYPT_MODE);
}
}
So now I have encrypted and stored the data. Now I want to pull it out...
String secString = settings.getString(SEC_KEY, null);
if (secString == null) {
try {
CryptoTranslator.generateKey();
settings.edit()
.putString(SEC_KEY,
CryptoTranslator.getSEC_KEY_String()).commit();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
CryptoTranslator.setSEC_KEY_STRING(secString);
}
try {
getUserNamePassword();
} catch (Exception ex) {
Log.i("Preferences",
"There was an issue getting username and password");
isStored = CRED_STATUS_DEF;
}
...
private static void getUserNamePassword() throws Exception {
isStored = settings.getBoolean(CRED_STATUS, CRED_STATUS_DEF);
if (isStored) {
if (settings.contains(USERNAME_KEY))
username = settings.getString(USERNAME_KEY, "");
if (settings.contains(PASSWORD_KEY))
password = settings.getString(PASSWORD_KEY, "");
}
isUsernamePasswordValid();
if (isStored) {
String username2 = CryptoTranslator.decrypt(username);
Log.d("Security", "Username encrypted");
String password2 = CryptoTranslator.decrypt(password);
username = username2;
password = password2;
Log.d("Security", "Password encrypted");
}
}
But this gives me the following error....
javax.crypto.IllegalBlockSizeException: last block incomplete in decryption
Can someone see what I am doing wrong?
Update
Ok per the response I went ahead and changed my code to the following...
public static final int IV_LENGTH = 16;
private static final String RANDOM_ALGORITHM = "SHA1PRNG";
...
private static String translate(String clear, int mode) throws Exception {
if (mode != Cipher.ENCRYPT_MODE && mode != Cipher.DECRYPT_MODE)
throw new IllegalArgumentException(
"Encryption invalid. Mode should be either Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE");
SecretKeySpec skeySpec = new SecretKeySpec(getRawKey(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivSpec = new IvParameterSpec(generateIv());
cipher.init(mode, skeySpec, ivSpec);
byte[] encrypted = cipher.doFinal(clear.getBytes());
return new String(encrypted);
}
...
private static byte[] generateIv() throws NoSuchAlgorithmException,
NoSuchProviderException {
SecureRandom random = SecureRandom.getInstance(RANDOM_ALGORITHM);
byte[] iv = new byte[IV_LENGTH];
random.nextBytes(iv);
return iv;
}
Now I get...
javax.crypto.BadPaddingException: pad block corrupted
To try and use hex changed to...
private static byte[] translate(byte[] val, int mode) throws Exception {
if (mode != Cipher.ENCRYPT_MODE && mode != Cipher.DECRYPT_MODE)
throw new IllegalArgumentException(
"Encryption invalid. Mode should be either Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE");
SecretKeySpec skeySpec = new SecretKeySpec(getRawKey(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivSpec = new IvParameterSpec(generateIv());
cipher.init(mode, skeySpec, ivSpec);
byte[] encrypted = cipher.doFinal(val);
return encrypted;
}
This seems to almost work (I am getting the .com back) but the chars are still pretty jumbled.
public static String encrypt(String clear) throws Exception {
byte[] test = translate(clear.getBytes(), Cipher.ENCRYPT_MODE);
return new String(Hex.encodeHex(test));
}
public static String decrypt(String encrypted) throws Exception {
return new String(translate(Hex.decodeHex(encrypted.toCharArray()), Cipher.DECRYPT_MODE));
}
*The converting to Hex and back is screwed up here.
So there are a couple of issues with your code.
First is the output of an AES cipher is not character data, you are mangling your ciphertext by trying to put it in a String. When you try to decrypt your mangled ciphertext it is now the wrong length. You need to Base64 or Hex encode the ciphertext if you want to store it in a String and then decode it back in to a byte[] before decrypting it.
Second, when you specify just AES for your cipher spec Java expands that to AES/ECB/PKCS5Padding. ECB is an insecure cipher mode if you intend to encrypt more than 1 block of data (16 bytes for AES). I recommend you switch to a different spec AES/CBC/PKCS5Padding should be acceptable. Using a mode other than ECB will require an Initialization Vector (IV). The IV should be randomly generated but does not need to be secret, so you can store as plaintext with your ciphertext as you'll need it to decrypt as well. The initialization vector needs to be one block in length (16 bytes for AES). Do not reuse the same IV with the same AES key ever, generate a new IV for each encryption being done.
Finally, if your going to store IV + ciphertext in a third party service I recommend you add a MAC (such as HMACSHA1). A MAC will ensure the integrity of your IV + ciphertext before you attempt to decrypt it. A MAC will require a secret key as well, and you should not use the same key you generated for the cipher itself. You can prepend the generated MAC to your IV + ciphertext, so now you are storing MAC + IV + ciphertext.
Android AES client side + PHP AES server side it will throw this error :)
The solution is:
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
Please search over the internet for the full source code. I am under NDA and to lazzy to make anonymous my whole code regarding this part, but I am sure you will find it.