使用 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 编码进行加密和解密的主要内容,如果未能解决你的问题,请参考以下文章

Python AES256解密算法

c语言中的openssl aes解密

MD5加密,Base64加密/解密,AES加密/解密

使用 AES 和 Base64 加密字符串

前端js 加密解密方式

Python AES - base64 加解密