使用 AES 和 Base64 编码进行加密和解密
Posted
技术标签:
【中文标题】使用 AES 和 Base64 编码进行加密和解密【英文标题】:Encrypt and decrypt with AES and Base64 encoding 【发布时间】:2011-04-26 15:48:44 【问题描述】:我有以下加密数据的程序。
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class Test
private static final String ALGORITHM = "AES";
private static final byte[] keyValue = "ADBSJHJS12547896".getBytes();
public static void main(String args[]) throws Exception
String encriptValue = encrypt("dude5");
decrypt(encriptValue);
/**
* @param args
* @throws Exception
*/
public static String encrypt(String valueToEnc) throws Exception
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.ENCRYPT_MODE, key);
System.out.println("valueToEnc.getBytes().length "+valueToEnc.getBytes().length);
byte[] encValue = c.doFinal(valueToEnc.getBytes());
System.out.println("encValue length" + encValue.length);
byte[] encryptedByteValue = new Base64().encode(encValue);
String encryptedValue = encryptedByteValue.toString();
System.out.println("encryptedValue " + encryptedValue);
return encryptedValue;
public static String decrypt(String encryptedValue) throws Exception
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.DECRYPT_MODE, key);
byte[] enctVal = c.doFinal(encryptedValue.getBytes());
System.out.println("enctVal length " + enctVal.length);
byte[] decordedValue = new Base64().decode(enctVal);
return decordedValue.toString();
private static Key generateKey() throws Exception
Key key = new SecretKeySpec(keyValue, ALGORITHM);
return key;
我在这里得到以下异常输出?
valueToEnc.getBytes().length 5
encValue length16
encryptedValue [B@aa9835
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
谁能解释一下原因?为什么它在解密时只说长度应该是 16。它不会像使用 doFinal 方法加密一样转换为 16。
正如例外所说“如何在没有填充密码的情况下解密?”
【问题讨论】:
你是对的,除了 2 分。我在另一个答案中提到过.. 【参考方案1】:您的加密订单: getBytes、加密、编码、toString您的解密订单(错误*): getBytes、解密、解码、toString
两个问题:
-
正如有人已经提到的,您应该颠倒解密操作的顺序。你没有那样做。
encrypt 给你 16 个字节,encode 24 个字节,但是 toString 给你 106 个字节。与占用额外空间的无效字符有关。
注意:另外,您不需要拨打generateKey()
两次。
修复问题 #1,使用相反的解密顺序。正确的解密顺序: getBytes、decode、decrypt、toString
修复问题 #2,将 xxx.toString()
替换为 new String(xxx)
。在加密和解密函数中都这样做。
您的解密应如下所示:
c.init(Cipher.DECRYPT_MODE, key)
val decodedValue = new Base64().decode(encryptedValue.getBytes())
val decryptedVal = c.doFinal(decodedValue)
return new String(decryptedVal)
这应该还给你“dude5”
【讨论】:
在答案的顶部,您将解密顺序指定为:getBytes、解密、解码、toString。然后你给出正确的顺序:getBytes、decode、decrypt、toString。我相信第二次排序是正确的。 Magnus,在我的回答顶部,当我提到“您的订单...”时,这就是 OP 执行序列的顺序。 “你的”指的是 OP。这不是“我的”命令。 还需要注意的是,从 byte[] 创建字符串时应始终使用特定的字符集,反之亦然:例如new String(bytes, "UTF-8")
和 string.getBytes("UTF-8")
。这确保了如果在具有不同系统字符集的不同系统上执行加密和解密,这不会导致失败。【参考方案2】:
线
String encryptedValue = encryptedByteValue.toString();
是问题所在。 encryptedByteValue 的类型是 byte[] 并且在它上面调用 toString 不是你想要做的。而是尝试
String encryptedValue = Base64.getEncoder().encodeToString(encValue);
然后在解密时使用Base64.decodeBase64(encryptedValue)
。但是,您必须在尝试解密之前执行此操作。您必须按照与 encrypt 方法相反的顺序撤消操作。
【讨论】:
它只是添加在那里从方法返回一个字符串..同样在 Base64 中没有这样的方法 encodeToString 关于 toString,在 Array 上调用该方法几乎不是您想要做的。它返回对象在内存中的地址,而不是有用的字符串表示。关于Base64,你不是用这个吗? commons.apache.org/codec/apidocs/org/apache/commons/codec/… 方法见这里:commons.apache.org/codec/apidocs/org/apache/commons/codec/… 我把 commons-codec-1.2 也尝试了 1.3 jar 但似乎方法不显示知道.. 根据我链接到的 Javadoc,它在 commons-code 1.4 中。 我没有找到任何方法 Base64.encodeToString(),但只使用 new String(encValue) 为我解决了问题。【参考方案3】:没关系,你只需要
1) 使用 new String 而不是 toString() 因为 toString() 不会返回您需要的内容(在这两种情况下,加密和解密)
2) 你需要先解码,因为值是用 base64 编码的。
我遇到了这个帖子,但我花了一些时间才找到真正的意义。我将我的代码发布给遇到这个问题的其他人。
public abstract class EncryptionDecryption
static byte[] key = "!@#$!@#$%^&**&^%".getBytes();
final static String algorithm="AES";
public static String encrypt(String data)
byte[] dataToSend = data.getBytes();
Cipher c = null;
try
c = Cipher.getInstance(algorithm);
catch (NoSuchAlgorithmException e)
// TODO Auto-generated catch block
e.printStackTrace();
catch (NoSuchPaddingException e)
// TODO Auto-generated catch block
e.printStackTrace();
SecretKeySpec k = new SecretKeySpec(key, algorithm);
try
c.init(Cipher.ENCRYPT_MODE, k);
catch (InvalidKeyException e)
// TODO Auto-generated catch block
e.printStackTrace();
byte[] encryptedData = "".getBytes();
try
encryptedData = c.doFinal(dataToSend);
catch (IllegalBlockSizeException e)
// TODO Auto-generated catch block
e.printStackTrace();
catch (BadPaddingException e)
// TODO Auto-generated catch block
e.printStackTrace();
byte[] encryptedByteValue = new Base64().encode(encryptedData);
return new String(encryptedByteValue);//.toString();
public static String decrypt(String data)
byte[] encryptedData = new Base64().decode(data);
Cipher c = null;
try
c = Cipher.getInstance(algorithm);
catch (NoSuchAlgorithmException e)
// TODO Auto-generated catch block
e.printStackTrace();
catch (NoSuchPaddingException e)
// TODO Auto-generated catch block
e.printStackTrace();
SecretKeySpec k =
new SecretKeySpec(key, algorithm);
try
c.init(Cipher.DECRYPT_MODE, k);
catch (InvalidKeyException e1)
// TODO Auto-generated catch block
e1.printStackTrace();
byte[] decrypted = null;
try
decrypted = c.doFinal(encryptedData);
catch (IllegalBlockSizeException e)
// TODO Auto-generated catch block
e.printStackTrace();
catch (BadPaddingException e)
// TODO Auto-generated catch block
e.printStackTrace();
return new String(decrypted);
public static void main(String[] args)
String password=EncryptionDecryption.encrypt("password123");
System.out.println(password);
System.out.println(EncryptionDecryption.decrypt(password));
【讨论】:
【参考方案4】:我已经替换了示例中的行:
String encryptedValue = encryptedByteValue.toString();
下一个:
String encryptedValue = new String(encryptedByteValue);
一切正常!
【讨论】:
我严重怀疑这是否修复了填充异常;您更有可能运行的是不同的 JVM。【参考方案5】:您从哪里获得具有 encodeToString 或 encodeBase64String 的 apache 编解码器版本?
我从 apache 网站下载了 1.5,虽然它在文档中说这些方法存在,但是当您执行代码完成时它们不会显示,并且当您提供它们时它们会创建一个未知方法。
我能做到:
byte raw[] = md.digest(); //step 4
byte hashBytes[] = Base64.encodeBase64(raw); //step 5
StringBuffer buffer = new StringBuffer();
for( int i=0; i<hashBytes.length; i++ )
buffer.append(hashBytes[i]);
return buffer.toString(); //step 6
然后我得到的字符串很长,但是解密正确。
我认为这不是“正确”的做事方式,但找不到文档所说的方法。
【讨论】:
【参考方案6】:从根本上说,您的加密函数和解密函数之间存在不对称性。加密时执行 AES 加密,然后执行 base64 编码,解密时不会先撤消 base64 编码步骤。
我认为你的 base64 编码有问题,[
不应该出现在 base64 编码的字符串中。
查看org.apache.commons.codec.binary.Base64
的文档,您应该能够在编码上做到这一点:
String encryptedValue = Base64.encodeBase64String(encValue);
这在解码:
byte[] encValue = Base64.decodeBase64(encryptedValue);
【讨论】:
@Harshana:那这是什么? commons.apache.org/codec/apidocs/org/apache/commons/codec/…以上是关于使用 AES 和 Base64 编码进行加密和解密的主要内容,如果未能解决你的问题,请参考以下文章