Java加密算法 AES

2018-03-21 22:28:02
package com.stone.security;

import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;

/**
 * AES 算法 对称加密,密码学中的高级加密标准 2005年成为有效标准 
 * @author stone
 * @date 2014-03-10 06:49:19
 */
public class AES {
	static Cipher cipher;
	static final String KEY_ALGORITHM = "AES";
	static final String CIPHER_ALGORITHM_ECB = "AES/ECB/PKCS5Padding";
	/*
	 * 
	 */
	static final String CIPHER_ALGORITHM_CBC = "AES/CBC/PKCS5Padding";
	/* 
	 * AES/CBC/NoPadding 要求
	 * 密钥必须是16字节长度的;Initialization vector (IV) 必须是16字节
	 * 待加密内容的字节长度必须是16的倍数,如果不是16的倍数,就会出如下异常:
	 * javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
	 * 
	 *  由于固定了位数,所以对于被加密数据有中文的, 加、解密不完整
	 *  
	 *  可 以看到,在原始数据长度为16的整数n倍时,假如原始数据长度等于16*n,则使用NoPadding时加密后数据长度等于16*n,
	 *  其它情况下加密数据长 度等于16*(n+1)。在不足16的整数倍的情况下,假如原始数据长度等于16*n+m[其中m小于16],
	 *  除了NoPadding填充之外的任何方 式,加密数据长度都等于16*(n+1).
	 */
	static final String CIPHER_ALGORITHM_CBC_NoPadding = "AES/CBC/NoPadding"; 
	
	static SecretKey secretKey;
		
	public static void main(String[] args) throws Exception {
		method1("a*jal)k32J8czx囙国为国宽");
		method2("a*jal)k32J8czx囙国为国宽");
		method3("a*jal)k32J8czx囙国为国宽");
		
		method4("123456781234囙为国宽");// length = 16
		method4("12345678abcdefgh");// length = 16
		
	}
	
	/**
	 * 使用AES 算法 加密,默认模式  AES/ECB
	 */
	static void method1(String str) throws Exception {
		cipher = Cipher.getInstance(KEY_ALGORITHM);
		//KeyGenerator 生成aes算法密钥
		secretKey = KeyGenerator.getInstance(KEY_ALGORITHM).generateKey();
		System.out.println("密钥的长度为:" + secretKey.getEncoded().length);
		
		cipher.init(Cipher.ENCRYPT_MODE, secretKey);//使用加密模式初始化 密钥
		byte[] encrypt = cipher.doFinal(str.getBytes()); //按单部分操作加密或解密数据,或者结束一个多部分操作。
		
		System.out.println("method1-加密:" + Arrays.toString(encrypt));
		cipher.init(Cipher.DECRYPT_MODE, secretKey);//使用解密模式初始化 密钥
		byte[] decrypt = cipher.doFinal(encrypt);
		System.out.println("method1-解密后:" + new String(decrypt));
		
	}
	
	/**
	 * 使用AES 算法 加密,默认模式 AES/ECB/PKCS5Padding
	 */
	static void method2(String str) throws Exception {
		cipher = Cipher.getInstance(CIPHER_ALGORITHM_ECB);
		//KeyGenerator 生成aes算法密钥
		secretKey = KeyGenerator.getInstance(KEY_ALGORITHM).generateKey();
		System.out.println("密钥的长度为:" + secretKey.getEncoded().length);
		
		cipher.init(Cipher.ENCRYPT_MODE, secretKey);//使用加密模式初始化 密钥
		byte[] encrypt = cipher.doFinal(str.getBytes()); //按单部分操作加密或解密数据,或者结束一个多部分操作。
		
		System.out.println("method2-加密:" + Arrays.toString(encrypt));
		cipher.init(Cipher.DECRYPT_MODE, secretKey);//使用解密模式初始化 密钥
		byte[] decrypt = cipher.doFinal(encrypt);
		System.out.println("method2-解密后:" + new String(decrypt));
		
	}
	
	static byte[] getIV() {
		String iv = "1234567812345678"; //IV length: must be 16 bytes long
		return iv.getBytes();
	}
	
	/**
	 * 使用AES 算法 加密,默认模式 AES/CBC/PKCS5Padding
	 */
	static void method3(String str) throws Exception {
		cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC);
		//KeyGenerator 生成aes算法密钥
		secretKey = KeyGenerator.getInstance(KEY_ALGORITHM).generateKey();
		System.out.println("密钥的长度为:" + secretKey.getEncoded().length);
		
		cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(getIV()));//使用加密模式初始化 密钥
		byte[] encrypt = cipher.doFinal(str.getBytes()); //按单部分操作加密或解密数据,或者结束一个多部分操作。
		
		System.out.println("method3-加密:" + Arrays.toString(encrypt));
		cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(getIV()));//使用解密模式初始化 密钥
		byte[] decrypt = cipher.doFinal(encrypt);
		System.out.println("method3-解密后:" + new String(decrypt));
		
	}
	
	/**
	 * 使用AES 算法 加密,默认模式 AES/CBC/NoPadding  参见上面对于这种mode的数据限制
	 */
	static void method4(String str) throws Exception {
		cipher = Cipher.getInstance(CIPHER_ALGORITHM_CBC_NoPadding);
		//KeyGenerator 生成aes算法密钥
		secretKey = KeyGenerator.getInstance(KEY_ALGORITHM).generateKey();
		System.out.println("密钥的长度为:" + secretKey.getEncoded().length);
		
		cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(getIV()));//使用加密模式初始化 密钥
		byte[] encrypt = cipher.doFinal(str.getBytes(), 0, str.getBytes().length); //按单部分操作加密或解密数据,或者结束一个多部分操作。
		
		System.out.println("method4-加密:" + Arrays.toString(encrypt));
		cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(getIV()));//使用解密模式初始化 密钥
		byte[] decrypt = cipher.doFinal(encrypt);
		
		System.out.println("method4-解密后:" + new String(decrypt));
		
	}
	
}


使用32字节(256位)密钥 

前文,method1-4,打印出来的 密钥长度都是16字节长度;都是通过KeyGenerator自动生成的。

jdk中 AES中的Cipher,对应的密钥长度为128位的,按8bits一个字节算,整好就是16个字节。



如果要指定为32字节长度(256位)密钥,如下实现:


[java] view plain copy

  1. /** 

  2.  * 指定密钥长度为32位字节。 默认只能16位,是因为美国的出口政策限制。 

  3.  * @param key_32 

  4.  * @param str 

  5.  * @throws Exception 

  6.  */  

  7. static void method5(String key_32, String str) throws Exception {  

  8.     System.out.println();  

  9.     cipher = Cipher.getInstance(CIPHER_ALGORITHM_ECB);  

  10.     secretKey = new SecretKeySpec(key_32.getBytes("utf-8"), "AES");  

  11.     System.out.println("密钥的长度为:" + secretKey.getEncoded().length);  

  12.   

  13.     cipher.init(Cipher.ENCRYPT_MODE, secretKey);  

  14.     byte[] encrypt = cipher.doFinal(str.getBytes(), 0, str.getBytes().length);  

  15.   

  16.     System.out.println("method5-加密:" + Arrays.toString(encrypt));  

  17.     cipher.init(Cipher.DECRYPT_MODE, secretKey);//使用解密模式初始化 密钥  

  18.     byte[] decrypt = cipher.doFinal(encrypt);  

  19.     System.out.println("method5-解密后:" + new String(decrypt, "utf-8"));  

  20. }  


调用代码:

[java] view plain copy

  1. String key_32 = "0123456789abcdef0123456789ABCDEF";  

  2. method5(key_32, "123456781234囙为国宽");  


如上,通过SecretkeySpec,可以指定32位字节密钥。

但是,运行后,发现报错了:"java.security.InvalidKeyException: Illegal key size or default parameters…"

网络搜索后发现如下解决方法:http://blog.csdn.net/l1028386804/article/details/42706039

因我的环境是jdk8,在oracle搜索到jce-jdk8的下载页面:http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html


最后,大功告成:


[plain] view plain copy

  1. 密钥的长度为:32  

  2. method5-加密:[-9, -118, -4, 2, 76, 48, 60, 4, -100, 19, 24, -14, 93, -20, -19, 77, -79, 105, -78, 53, 17, 29, -65, 36, 104, -60, 83, 31, -76, -26, 54, -120]  

  3. method5-解密后:123456781234囙为国宽  


如果像安卓app,要在客户端实现32位字节密钥加、解密,估计还是不行的;只能考虑自己实现一套AES的算法了,同时服务端也使用相应算法



使用"AES/CBC/NoPadding"引发的问题


不考虑上面的256位密钥问题,在默认情况下,这种padding,要求 密钥、IV长度都是16字节,内容字节长度必须是16的倍数,如果不是16的倍数,则引发异常。

如果一定要使用这种padding,而无法保证内容为16字节的倍数,可以考虑自实现一套规则,补全内容长度为16*n


  • 2020-12-16 06:43:06

    WebRTC 实现Android点到点互连(含Demo)

    WebRTC被誉为是web长期开源开发的一个新启元,是近年来web开发的最重要创新。WebRTC允许Web开发者在其web应用中添加视频聊天或者点对点数据传输,不需要复杂的代码或者昂贵的配置。目前支持Chrome、Firefox和Opera,后续会支持更多的浏览器,它有能力达到数十亿的设备。

  • 2020-12-16 22:04:03

    基于本地代理的边下边播技术分析

    我们熟知的边下边播技术,是迅雷提供的,还有之前的快播、快车等工具,它们使用的技术基本上都是P2P下载技术。 P2P下载技术,本质上它并不是C-S的架构,P2P----> Peer to Peer,实际上它将各个客户端的资源调度起来,给上传资源种子,方便后续的下载者可以快速有效的下载资源,这种方式需要服务器整合各个Client,在有用户需要下载的情况下,服务器能及时调度资源,开始给下载者提供资源信息,保证下载者下载资源越快越好。P2P的下载方式后面我们专门介绍一下。这儿不继续展开了。

  • 2020-12-16 22:07:44

    Android视频点播-边播边缓存

    一些知名的视频app客户端(优酷,爱奇艺)播放视频的时候都有一些缓存进度(二级进度缓存),qq,微信有关的小视频,还有一些短视频app,都有边播边缓的处理。还有就是当文件缓存完毕了再次播放的话就不再请求网络了直接播放本地文件了。既节省了流程又提高了加载速度。 今天我们就是来研究讨论实现这个边播边缓存的框架,因为它不和任何的业务逻辑耦合。

  • 2020-12-16 22:46:44

    基于coturn项目的stun/turn服务器搭建

    webrtc是google推出的基于浏览器的实时语音-视频通讯架构。其典型的应用场景为:浏览器之间端到端(p2p)实时视频对话,但由于网络环境的复杂性(比如:路由器/交换机/防火墙等),浏览器与浏览器很多时候无法建立p2p连接,只能通过公网上的中继服务器(也就是所谓的turn服务器)中转。示例图如下:

  • 2020-12-16 23:06:05

    Rocket.Chat推送信息

    Rocket.Chat推送消息 Rocket.Chat是一个开源实时通讯平台, 支持Windows, Mac OS, Linux. 支持聊天, 文件上传, 视频通话, 语音通话功能. 向Rocket.Chat推送消息 以下示例可以转为别的语言的版本, 本示例使用Linux平台的curl测试, curl非常强大. 登陆 首先需要登陆Rocket.Chat服务器

  • 2020-12-17 09:01:23

    对BitTorrent Tracker源码分析

    tracker服务器是BT下载中必须的角色。一个BT client 在下载开始以及下载进行的过程中,要不停的与 tracker 服务器进行通信,以报告自己的信息,并获取其它下载client的信息。这种通信是通过 HTTP 协议进行的,又被称为 tracker HTTP 协议,它的过程是这样的: client 向 tracker 发一个HTTP 的GET请求,并把它自己的信息放在GET的参数中;这个请求的大致意思是:我是xxx(一个唯一的id),我想下载yyy文件,我的ip是aaa,我用的端口是bbb。。。

  • 2020-12-17 10:55:48

    html5 video p2p research

    节约带宽,减少缓冲时间,提升服务质量,处理峰值流量, 视频观看的人越多,播放越流畅。