Java实现AES ECP PKCS5Padding加解密工具类

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: Java 实现一个AES/ECB/PKCS5Padding 加解密算法工具类加密算法: AES模式: ECB补码方式: PKCS5Padding

Java 实现一个AES/ECB/PKCS5Padding 加解密算法工具类


  • 加密算法: AES
  • 模式: ECB
  • 补码方式: PKCS5Padding


1. 工具类



import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Base64Utils;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
/**
 * Created by @author yihui in 19:12 20/1/2.
 */
@Slf4j
public class EncryptUtil {
    private static final String KEY_ALGORITHM = "AES";
    /**
     * 算法/模式/补码方式
     */
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
    private static final String CODE = "utf-8";
    @Setter
    @Getter
    public static String encryptKey;
    public static String encrypt(String content) {
        return encrypt(content, encryptKey);
    }
    /**
     * 加密
     *
     * @param content
     * @param key
     * @return
     * @throws Exception
     */
    public static String encrypt(String content, String key) {
        try {
            byte[] encrypted = encrypt2bytes(content, key);
            return Base64Utils.encodeToString(encrypted);
        } catch (Exception e) {
            log.error("failed to encrypt: {} of {}", content, e);
            return null;
        }
    }
    public static byte[] encrypt2bytes(String content, String key) {
        try {
            byte[] raw = key.getBytes(CODE);
            SecretKeySpec secretKeySpec = new SecretKeySpec(raw, KEY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
            return cipher.doFinal(content.getBytes(CODE));
        } catch (Exception e) {
            log.error("failed to encrypt: {} of {}", content, e);
            return null;
        }
    }
    public static String decrypt(String content) {
        try {
            return decrypt(content, encryptKey);
        } catch (Exception e) {
            log.error("failed to decrypt: {}, e: {}", content, e);
            return null;
        }
    }
    /**
     * 解密
     *
     * @param content
     * @param key
     * @return
     * @throws Exception
     */
    public static String decrypt(String content, String key) throws Exception {
        return decrypt(Base64Utils.decodeFromString(content), key);
    }
    public static String decrypt(byte[] content, String key) throws Exception {
        if (key == null) {
            log.error("AES key should not be null");
            return null;
        }
        byte[] raw = key.getBytes(CODE);
        SecretKeySpec keySpec = new SecretKeySpec(raw, KEY_ALGORITHM);
        Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        try {
            byte[] original = cipher.doFinal(content);
            return new String(original, CqODE);
        } catch (Exception e) {
            log.error("failed to decrypt content: {}/ key: {}, e: {}", content, key, e);
            return null;
        }
    }
}
复制代码


请注意上面的实现,提供了两种方式


  • 一个是AES加密之后使用base64编码输出,对应的是解密base64编码的数据
  • 一个是AES加密之后,直接返回字节数组;也是直接解码字节数组


2. 测试case



我们提供了两个加密的文件,用于解密使用;


base64加解密

@Test
public void testEncrypt() throws Exception {
    String abc = "Hello, 一灰灰Blog!";
    String key = "JC66fRd3wj85k8Hr";
    String out = EncryptUtil.encrypt(abc, key);
    System.out.println(out);
    System.out.println(EncryptUtil.decrypt(out, key));
}
复制代码


输出结果如:

TKrN7VKrqsAQ4JqygeHOlG21Sd3IRJ3Y11k4kOdOG4s=
Hello, 一灰灰Blog!
复制代码


字节数组加解密

@Test
public void testEncryptByte() throws Exception {
    String abc = "Hello, 一灰灰Blog!";
    String key = "JC66fRd3wj85k8Hr";
    byte[] out = EncryptUtil.encrypt2bytes(abc, key);
    System.out.println(new String(out));
    System.out.println(EncryptUtil.decrypt(out, key));
}
复制代码


输出结果如:

// 加密的字节数组,就是乱码... 你没看错
L���R��������Δm�I��D���Y8��N�
Hello, 一灰灰Blog!
复制代码


为什么有上面两种区别?


如果我们将加密后的字节数组,直接 new String() 获得一个字符串,然后解密这个字符串,会发现解密失败哦


简单修改一下上面的测试用例

@Test
public void testEncryptByte() throws Exception {
    String abc = "Hello, 一灰灰Blog!";
    String key = "JC66fRd3wj85k8Hr";
    byte[] out = EncryptUtil.encrypt2bytes(abc, key);
    String enc = new String(out, "utf-8");
    System.out.println(enc);
    System.out.println(EncryptUtil.decrypt(enc.getBytes("utf-8"), key));
}
复制代码


执行之后,发现解密失败

image.png


为啥会出现这样情况呢?

  • enc = new String(out, "utf-8")enc.getBytes("utf-8") 字节数组转字符串; 字符串转字节数组这两个过程会导致最终生成的字节数组,与原始的不一致!!!


解密远程资源的case


最后给一个解密远程加密的二进制文件的实例case

private void binKey(String uri, String key) throws Exception {
    // 这个文件是没有base64编码,直接上传的二进制
    URL url = new URL(uri);
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    InputStream stream = connection.getInputStream();
    int lenth = connection.getContentLength();
    byte[] out = new byte[lenth];
    stream.read(out);
    stream.close();
    String ans = decrypt(out, key);
    System.out.println(ans);
}
public void testDe() throws Exception {
    String key = "5JRHMJn8xHnMDRXa";
    binKey("http://q8rnsprw0.bkt.clouddn.com/mwzz/b0001", key);
}



相关文章
|
3月前
|
算法 搜索推荐 Java
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
这篇文章介绍了如何使用Java后端技术,结合Graphics2D和Echarts等工具,生成包含个性化信息和图表的海报,并提供了详细的代码实现和GitHub项目链接。
161 0
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
|
3月前
|
Java
Java 些许公共工具类
Java 些许公共工具类
20 1
|
5月前
|
Java
Java应用结构规范问题之在UnitConvertUtils工具类将千米转换为米的问题如何解决
Java应用结构规范问题之在UnitConvertUtils工具类将千米转换为米的问题如何解决
|
5月前
|
存储 设计模式 安全
Java GenericObjectPool 对象池化技术--SpringBoot sftp 连接池工具类
Java GenericObjectPool 对象池化技术--SpringBoot sftp 连接池工具类
73 0
时间轮-Java实现篇
在前面的文章《[时间轮-理论篇](https://developer.aliyun.com/article/910513)》讲了时间轮的一些理论知识,然后根据理论知识。我们自己来实现一个简单的时间轮。
|
11天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
13天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
13天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
13天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
37 3
|
13天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
93 2