接口API中的敏感数据基于AES进行安全加密后返回

许久没有写博客了,有些惶恐地打开这个再熟悉不过的编辑器。

场景:要对一个涉及到敏感数据(账号、密码)的接口进行加密后返回

由于之前没有相关的经验,所以先在网上搜罗了一阵,这篇博客不错https://www.cnblogs.com/codeon/p/6123863.html给了我一些思路和启发。

先来介绍两个模糊不清,容易搞混的概念。

  • Base64编码,看名字就可以知道这是一种编码方式,编码方式有很多ASCII、Unicode、UTF-8等,Base64编码会把3字节的二进制数据编码为4字节的文本数据,长度增加为原来的4/3。一定要强调一下Base64不是安全领域下的加密解密算法,虽然有时候经常看到有些博客上和变换工具上讲base64加密解密。其实base64只能算是一个编码算法,对数据内容进行编码来适合传输。虽然base64编码过后原文也变成不能看到的字符格式,但是这种方式很初级,很简单。具体了解各个编码的场景可以参照这篇博客,https://blog.csdn.net/charleslei/article/details/50993861
  • MD5摘要算法,这是一种散列函数,提取数据的特征,输出是不可逆的散列值,用于代表某信息A而又不暴露信息A的内容,一般用于数字签名场景中。

加密方式的确定:最后我的接口中的敏感明文信息通过AES进行加密,最后将密文返回给客户端。

网上的一些AES加解密示例中,很多在AES解密步骤时会出现javax.crypto.BadPaddingException: Given final block not properly padded问题,而且很多答案都说的似是而非,经过我的调试和修正后,以下代码可以正常的完成AES/DES的加解密操作。

package com.test.utils;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * @Author: LeeChao
 * @Date: 2018/7/5
 * @Describe: 加密工具类
 * @Modified By:
 */
public class EncryptUtil {

    public static final Logger LOGGER = LogManager.getLogger(EncryptUtil.class);

    // 此处使用AES-128-CBC加密模式,key需要为16位。
    private static final String AES_ENCRYPT_MODE = "AES/CBC/PKCS5Padding";

    // 16固定的偏移向量
    private static final String IV_PARAMETER = "1234567890abcdef";

    private final static String DES_ENCRYPT_MODE = "DES";

    /**
     * AES加密
     *
     * @param aesKey  加密用的Key 可以用26个字母和数字组成,
     * @param content
     * @return
     * @throws Exception
     */
    public static String aesEncrypt(String aesKey, String content) {
        // 初始化返回结果
        String result = null;
        try {
            Cipher cipher = Cipher.getInstance(AES_ENCRYPT_MODE);
            byte[] raw = aesKey.getBytes();
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            // 使用CBC模式,需要一个向量iv,可增加加密算法的强度
            IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
            byte[] encrypted = cipher.doFinal(content.getBytes("utf-8"));
            // 此处使用BASE64做转码。
            result = new BASE64Encoder().encode(encrypted);
            // Base64加密后的换行符去掉
            result = result.replaceAll("\\r\\n", "").replaceAll("\r", "").replaceAll("\n", "");
        } catch (Exception e) {
            LOGGER.error("AES加密异常" + e);
        }
        return result;
    }

    /**
     * AES解密
     *
     * @param aesKey
     * @param content
     * @return
     * @throws Exception
     */
    public static String aesDecrypt(String aesKey, String content) {
        // 初始化返回结果
        String result = null;
        try {
            byte[] raw = aesKey.getBytes("ASCII");
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance(AES_ENCRYPT_MODE);
            IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] encrypted1 = new BASE64Decoder().decodeBuffer(content);//先用base64解密
            byte[] original = cipher.doFinal(encrypted1);
            result = new String(original, "utf-8");
        } catch (Exception e) {
            LOGGER.error("AES解密异常" + e);
        }
        return result;
    }

    /**
     * MD5散列为16位定长输出
     *
     * @param sourceStr
     * @return
     */
    public static String MD5(String sourceStr) {
        String result16 = "";
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(sourceStr.getBytes());
            byte b[] = md.digest();
            int i;
            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < b.length; offset++) {
                i = b[offset];
                if (i < 0)
                    i += 256;
                if (i < 16)
                    buf.append("0");
                buf.append(Integer.toHexString(i));
            }
            // 32位截取为16位
            result16 = buf.toString().substring(8, 24);
        } catch (NoSuchAlgorithmException e) {
            LOGGER.error(e);
        }
        return result16;
    }

    /********************DES加密方式***********************/
    public static byte[] desEncrypt(byte[] src, byte[] key) throws Exception {
        // DES算法要求有一个可信任的随机数源
        SecureRandom sr = new SecureRandom();
        // 从原始密匙数据创建DESKeySpec对象
        DESKeySpec dks = new DESKeySpec(key);
        // 创建一个密匙工厂,然后用它把DESKeySpec转换成一个SecretKey对象
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES_ENCRYPT_MODE);
        SecretKey securekey = keyFactory.generateSecret(dks);
        // Cipher对象实际完成加密操作
        Cipher cipher = Cipher.getInstance(DES_ENCRYPT_MODE);
        // 用密匙初始化Cipher对象
        cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
        // 正式执行加密操作
        return cipher.doFinal(src);
    }

    /**
     * @param password 密码
     * @param key      加密字符串
     * @return
     */
    public final static String desEncrypt(String password, String key) {
        try {
            return byte2String(desEncrypt(password.getBytes(), key.getBytes()));
        } catch (Exception e) {
        }
        return null;
    }

    public static String byte2String(byte[] b) {
        String hs = "";
        String stmp = "";
        for (int n = 0; n < b.length; n++) {
            stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
            if (stmp.length() == 1)
                hs = hs + "0" + stmp;
            else
                hs = hs + stmp;
        }
        return hs.toUpperCase();
    }

    /**
     * @param src 数据源
     * @param key 密钥,长度必须是8的倍数
     * @return
     * @throws Exception
     */
    public static byte[] desDecrypt(byte[] src, byte[] key) throws Exception {
        // DES算法要求有一个可信任的随机数源
        SecureRandom sr = new SecureRandom();
        // 从原始密匙数据创建一个DESKeySpec对象
        DESKeySpec dks = new DESKeySpec(key);
        // 创建一个密匙工厂,然后用它把DESKeySpec对象转换成一个SecretKey对象
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES_ENCRYPT_MODE);
        SecretKey securekey = keyFactory.generateSecret(dks);
        // Cipher对象实际完成解密操作
        Cipher cipher = Cipher.getInstance(DES_ENCRYPT_MODE);
        // 用密匙初始化Cipher对象
        cipher.init(Cipher.DECRYPT_MODE, securekey, sr);

        // 正式执行解密操作
        return cipher.doFinal(src);
    }

    public final static String desDecrypt(String data, String key) {
        try {
            return new String(desDecrypt(String2byte(data.getBytes()), key.getBytes()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static byte[] String2byte(byte[] b) {
        if ((b.length % 2) != 0)
            throw new IllegalArgumentException("长度不是偶数");
        byte[] b2 = new byte[b.length / 2];
        for (int n = 0; n < b.length; n += 2) {
            String item = new String(b, n, 2);
            b2[n / 2] = (byte) Integer.parseInt(item, 16);
        }
        return b2;
    }

    private static String testDecryptAES(String authCode, String cipherText) {
        System.out.println("=====================AES解密=====================");
        String key = MD5(authCode);
        // md5散列出16位固定长度的秘钥
        System.out.println("md5-16 :" + key);
        long encryptTime = System.currentTimeMillis();
        String deString = aesDecrypt(key, cipherText);
        long endTime = System.currentTimeMillis();
        System.out.println("解密耗时:" + (endTime - encryptTime) + "毫秒");
        System.out.println("解密后的字串是:" + deString);
        return deString;
    }

    public static void main(String[] args) throws Exception {

        // code
        String authCode = "8373964002824192";
        // config_info
        String cipherText = "iiK82bTFJLZupt0GuvPZ6vx/OrFdcnHrvVjC4rhPjQz15vFeVzV/TvotNZYi3K6V2NHMLwPdhd4YLQw1e/Wr78Jdemif/yMSvR6CmU5UtnrKs0QgDoOsepRxD5t996tzxW18n/SCsn++h1sokdzs8HvEI0tBP5Z0p5wMQRvi6rq1BadqCySbTGCg3LIpy0yeO/FXufZZw6A5yJ/PruOsGywyxHRwSLABMBR1C3HpsXPpXJkvQl6QlkKt38z2wY4K3Ag3+iI7e03kBVbbR/Omz8nir7s7EQzHgfbGlAZNAbA6iKAh/z6W2gToP4VNHPq2NcwCNdODK8TwR7bK3RD657rqYRJ/C7EBTLi+GpC8mTOOorloW9iEkRPB3pqkDk8r8awlJzi0DZXWxuOn+FNGdosm51qN+OJbuNGa7MiNANki8bhHYNV1Mlbh0sDF4vUsNwFjp1ZBldUu1dNUzbl7b2CWgAsYXhc4y2lsOnX8ddXGqXqXiv2n4JFjkgqBsJ3sapIk5WCTeIH8MjzygnXoHduSpXUNlpMVHsjp3xJLmtJEXYimfqOrL+gnbNwfUCQhu+lqNuSDx+nTqJAeQ7cP+c9YThNUjR8fslZSejnK2PJpIkXupmNjM+3t8D7J7kOYZOyRH5eymcL6h499lNYWf//xAx3RTuFSihvVt7ERjj2AVU/0zPABV5X4llBjVrSoJyDZQIDuYqPH3ChI+hwHjSbxw+RZlTtcorYv4fyXDM9/NepGFP3WWgXmnfwI5s/rfio4tsERLuUXTDza6FR9DA==";
        // 解密拿到明文
        testDecryptAES(authCode, cipherText);

        System.out.println("=====================AES加密=====================");
        String key = MD5("8373964002824192");
        // md5散列出16位固定长度的秘钥
        System.out.println("md5-16 :" + key);
        // 需要加密的字串
        String cSrc = "123abctest";
        // 加密
        long lStart = System.currentTimeMillis();
        String enString = aesEncrypt(key, cSrc);
        System.out.println("加密后的字串是:" + enString);

        long lUseTime = System.currentTimeMillis() - lStart;
        System.out.println("加密耗时:" + lUseTime + "毫秒");
        // 解密
        lStart = System.currentTimeMillis();
        String DeString = aesDecrypt(key, enString);
        System.out.println("解密后的字串是:" + DeString);
        lUseTime = System.currentTimeMillis() - lStart;
        System.out.println("解密耗时:" + lUseTime + "毫秒");

        System.out.println("=====================DES加密=====================");
        String password = "test中英文杂七烂八混搭@123654{";
        String info = "123abctest";
        long desStart = System.currentTimeMillis();
        String encryptString = desEncrypt(info, password);
        System.out.println(encryptString);
        System.out.println("DES加密耗时" + (System.currentTimeMillis() - desStart) + "毫秒");

        desStart = System.currentTimeMillis();
        String desencryptString = desDecrypt(encryptString, password);
        System.out.println(desencryptString);
        System.out.println("DES解密耗时" + (System.currentTimeMillis() - desStart) + "毫秒");
    }
}

  

原文地址:https://www.cnblogs.com/lingyejun/p/9270127.html

时间: 2024-12-25 02:52:08

接口API中的敏感数据基于AES进行安全加密后返回的相关文章

消息队列接口API(posix 接口和 system v接口)

消息队列 posix API 消息队列(也叫做报文队列)能够克服早期unix通信机制的一些缺点.信号这种通信方式更像\"即时\"的通信方式,它要求接受信号的进程在某个时间范围内对信号做出反应,因此该信号最多在接受信号进程的生命周期内才有意义,信号所传递的信息是接近于随进程持续的概念(process-persistent):管道及有名管道则是典型的随进程持续IPC,并且,只能传送无格式的字节流无疑会给应用程序开发带来不便,另外,它的缓冲区大小也受到限制消息队列就是一个消息的链表.可以把消

天气预报接口IOS版OC:SmartWeather API中key的计算方法

最近在做一个天气预报app,看见国家气象局有api接口提供,但是需要申请,网址 http://smart.weather.com.cn/wzfw/smart/weatherapi.shtml, 审核大概需要一周左右,审核通过后,你会收到一封邮件 您好: 欢迎使用SmartWeatherAPI测试接口 恭喜您的申请已通过审核,以下是为您分配的鉴权信息: appid:XXXXXXXXXXXXXXXX private_key:XXXXXXXXXXXXXXXX 接口使用说明请参考<SmartWeathe

Atitit 图像处理之编程之类库调用的接口api cli gui ws rest &#160;attilax大总结.docx

Atitit 图像处理之编程之类库调用的接口api cli gui ws rest  attilax大总结.docx 1. 为什么需要接口调用??1 1.1. 为了方便集成复用模块类库1 1.2. 嫁接不同的语言与类库,以及嵌入dsl1 1.3. 方便跨机器,跨开发板,跨硬件,跨运行环境的代码复用2 2. 接口api的历史2 2.1. 发展历程2 2.2. API 这个类库默认提供的接口,要求同语言调用一般2 2.3. Cli接口 命令行接口.单机跨语言接口(推荐比较常用)3 2.4. 图形用户

Web APi 2.0优点和特点?在Web APi中如何启动Session状态?

前言 曾几何时,微软基于Web服务技术给出最流行的基于XML且以扩展名为.asmx结尾的Web Service,此服务在.NET Framework中风靡一时同时也被.NET业界同仁所青睐,几年后在此基础上又扩展成为了WCF,基于SOAP协议,基于WCF标准需要一些配置上的改变.现如今,大势所趋我们只需要HTTP协议以及更加优美的JSON格式,这时将不得不出现一个更加轻量级的Web服务技术.当然,Web Service和WCF虽然有其局限性但是其仍被许多企业所广泛应用,说明一时半会还不会被淘汰,

nodejs api 中文文档

文档首页 英文版文档 本作品采用知识共享署名-非商业性使用 3.0 未本地化版本许可协议进行许可. Node.js v0.10.18 手册 & 文档 索引 | 在单一页面中浏览 | JSON格式 目录 关于本文档 稳定度 JSON 输出 概述 全局对象 global process console 类: Buffer require() require.resolve() require.cache require.extensions __filename __dirname module e

App开放接口api安全性—Token签名sign的设计与实现

前言 在app开放接口api的设计中,避免不了的就是安全性问题,因为大多数接口涉及到用户的个人信息以及一些敏感的数据,所以对这些 接口需要进行身份的认证,那么这就需要用户提供一些信息,比如用户名密码等,但是为了安全起见让用户暴露的明文密码次数越少越好,我们一般在web项目 中,大多数采用保存的session中,然后在存一份到cookie中,来保持用户的回话有效性.但是在app提供的开放接口中,后端服务器在用户登录后 如何去验证和维护用户的登陆有效性呢,以下是参考项目中设计的解决方案,其原理和大多

【Web API系列教程】2.2 — ASP.NET Web API中的路由和动作选择机制

这篇文章描述了ASP.NET Web API如何将HTTP请求路由到控制器上的特定动作. 备注:想要了解关于路由的高层次概述,请查看Routing in ASP.NET Web API. 这篇文章侧重于路由过程的细节.如果你创建了一个Web API项目并且发现一些请求并没有按你预期得到相应的路由,希望这篇文章有所帮助. 路由有以下三个主要阶段: 将URI匹配到路由模板 选择一个控制器 选择一个动作 你可以用自己的习惯行为来替换其中一些过程.在本文中,我会描述默认行为.在结尾,我会指出你可以自定义

在ASP.NET Core Web API中为RESTful服务增加对HAL的支持

HAL(Hypertext Application Language,超文本应用语言)是一种RESTful API的数据格式风格,为RESTful API的设计提供了接口规范,同时也降低了客户端与服务端接口的耦合度.很多当今流行的RESTful API开发框架,包括Spring REST,也都默认支持HAL规范,当RESTful API被调用后,服务端就会返回ContentType为application/hal+json的JSON内容,例如: { "_links": { "

python3.8.0 Django 开发后端接口api 部署到 Linux Centos7上

经历了两天的时候终于把本地使用python3 django开发的接口API部署到服务器上了,还是记录一下,以免之后忘记,哈哈 注意一点,就是,centos7是基于python2的,我这边默认的是python2.7.5,记住不要删除了python2,否则后果很严重,yum会报错,这是因为python3和python2版本的冲突导致的 第一步,安装 python3 到 Centos7, 先安装软件管理包和可能使用的依赖 yum -y groupinstall "Development tools&q