1、Java的安全体系架构
Java中为安全框架提供类和接口。JDK 安全 API 是 Java 编程语言的核心 API,位于 java.security 包(及其子包),以及sun.securityAPI包(及其子包)中。设计用于帮助开发人员在程序中同时使用低级和高级安全功能。
JDK 1.1 中第一次发布的 JDK 安全中引入了“Java 加密体系结构”(JCA),指的是用于访问和开发 Java 平台密码功能的构架。在 JDK 1.1 中,JCA 包括用于数字签名和报文摘要的 API。JDK 1.2 大大扩展了 Java 加密体系结构,它还对证书管理基础结构进行了升级以支持 X.509 v3 证书,并为划分细致、可配置性强、功能灵活、可扩展的访问控制引入了新的 Java 安全体系结构。
Java 加密体系结构包含 JDK 1.2 安全 API 中与密码有关的部分,以及本文档中提供的一组约定和规范。为实现多重、可互操作的密码,它还提供了“提供者”体系结构。
Java 密码扩展 (JCE))扩展了 JCA API,包括用于加密、密钥交换和信息认证码(MAC)的 API。JCE 和 JDK 密码共同提供了一个与平台无关的完整密码 API。JCE 作为 JDK 的扩展将独立发布,以符合美国的出口控制约束。
在JDK6.0中,与MD5与SHA密切相关的几个类的类图如下:
其中“MessageDigestSpi”为顶层抽象类,同一个包下的“MessageDigest”和“DigestBase”为子抽象类。
在上面的类图中,使用了Delegate(委托)设计模式。这种模式的原理为类B(在此处为Delegage内部类)和类A(在此处为MessageDigestSpi类)是两个互相没有什么关系的类,B具有和A一模一样的方法和属性;并且调用B中的方法和属性就是调用A中同名的方法和属性。B好像就是一个受A授权委托的中介。第三方的代码不需要知道A及其子类的存在,也不需要和A及其子类发生直接的联系,通过B就可以直接使用A的功能,这样既能够使用到A的各种功能,又能够很好的将A及其子类保护起来了。
MD5和SHA的相关代码都在MD5和SHA等类中,但是面向客户的MessageDigest抽象类不需要跟各个实现类打交道,只要通过委托类与其打交道即可。
二、单向加密算法MD5和SHA
2.1 MD5加密
2.1.1 概述
Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。该算法的文件号为RFC 1321(R.Rivest,MIT Laboratory for Computer Science and RSA Data Security Inc. April 1992).
MD5的全称是Message-Digest Algorithm 5(信息-摘要算法),在90年代初由MIT Laboratory for Computer Science和RSA Data Security Inc的Ronald L. Rivest开发出来,经MD2、MD3和MD4发展而来。
MD5用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,MD5的前身有MD2、MD3和MD4。
MD5的作用是让大容量信息在用数字签名软件签署私人密钥前被"压缩"成一种保密的格式(就是把一个任意长度的字节串变换成一定长的十六进制数字串)。
2.1.2 算法原理
对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。
在MD5算法中,首先需要对信息进行填充,使其位长对512求余的结果等于448。因此,信息的位长(Bits Length)将被扩展至N512+448,N为一个非负整数,N可以是零。填充的方法如下,在信息的后面填充一个1和无数个0,直到满足上面的条件时才停止用0对信息的填充。然后,在这个结果后面附加一个以64位二进制表示的填充前信息长度。经过这两步的处理,信息的位长=N512+448+64=(N+1)*512,即长度恰好是512的整数倍。这样做的原因是为满足后面处理中对信息长度的要求。
2.1.3 Java中的MD5实现
MD5加密算法的Java实现如下所示:
import java.security.MessageDigest;/** * 采用MD5加密* @author Xingxing,Xie* @datetime 2014-5-31 */
public class MD5Util {/*** * MD5加密 生成32位md5码* @param 待加密字符串* @return 返回32位md5码*/public static String md5Encode(String inStr) throws Exception {MessageDigest md5 = null;try {md5 = MessageDigest.getInstance("MD5");} catch (Exception e) {System.out.println(e.toString());e.printStackTrace();return "";}byte[] byteArray = inStr.getBytes("UTF-8");byte[] md5Bytes = md5.digest(byteArray);StringBuffer hexValue = new StringBuffer();for (int i = 0; i < md5Bytes.length; i++) {int val = ((int) md5Bytes[i]) & 0xff;if (val < 16) {hexValue.append("0");}hexValue.append(Integer.toHexString(val));}return hexValue.toString();}/*** 测试主函数* @param args* @throws Exception*/public static void main(String args[]) throws Exception {String str = new String("amigoxiexiexingxing");System.out.println("原始:" + str);System.out.println("MD5后:" + md5Encode(str));}
}
测试结果:
原始:amigoxiexiexingxingMD5后:e9ac094091b96b84cca48098bc21b1d6
2.2 SHA加密
2.2.1 概述
SHA是一种数据加密算法, 该算法经过加密专家多年来的发展和改进已日益完善,现在已成为公认的最安全的散列算法之一,并被广泛使用。该算法的思想是接收一段明文,然后以一种不可逆 的方式将它转换成一段(通常更小)密文,也可以简单的理解为取一串输入码(称为预映射或信息),并把它们转化为长度较短、位数固定的输出序列即散列值(也 称为信息摘要或信息认证代码)的过程。散列函数值可以说是对明文的一种“指纹”或是“摘要”所以对散列值的数字签名就可以视为对此明文的数字签名。
安全散列算法SHA(Secure Hash Algorithm,SHA)是美国国家标准技术研究所发布的国家标准FIPS PUB 180,最新的标准已经于2008年更新到FIPS PUB 180-3。其中规定了SHA-1,SHA-224,SHA-256,SHA-384,和SHA-512这几种单向散列算法。SHA-1,SHA-224和SHA-256适用于长度不超过2^64 二进制位的消息。SHA-384和SHA-512适用于长度不超过2^128 二进制位的消息。
2.2.2 原理
SHA-1是一种数据加密算法,该算法的思想是接收一段明文,然后以一种不可逆的方式将它转换成一段(通常更小)密文,也可以简单的理解为取一串输入码(称为预映射或信息),并把它们转化为长度较短、位数固定的输出序列即散列值(也称为信息摘要或信息认证代码)的过程。
单向散列函数的安全性在于其产生散列值的操作过程具有较强的单向性。如果在输入序列中嵌入密码,那么任何人在不知道密码的情况下都不能产生正确的散列值,从而保证了其安全性。SHA将输入流按照每块512位(64个字节)进行分块,并产生20个字节的被称为信息认证代码或信息摘要的输出。
该算法输入报文的长度不限,产生的输出是一个160位的报文摘要。输入是按512 位的分组进行处理的。SHA-1是不可逆的、防冲突,并具有良好的雪崩效应。
通过散列算法可实现数字签名实现,数字签名的原理是将要传送的明文通过一种函数运算(Hash)转换成报文摘要(不同的明文对应不同的报文摘要),报文摘要加密后与明文一起传送给接受方,接受方将接受的明文产生新的报文摘要与发送方的发来报文摘要解密比较,比较结果一致表示明文未被改动,如果不一致表示明文已被篡改。
MAC (信息认证代码)就是一个散列结果,其中部分输入信息是密码,只有知道这个密码的参与者才能再次计算和验证MAC码的合法性。
2.2.3 Java中SHA实现
SHA的在Java的实现与MD5类似,参考代码如下所示:
import java.security.MessageDigest;/** * 采用SHAA加密* @author Xingxing,Xie* @datetime 2014-6-1 */
public class SHAUtil {/*** * SHA加密 生成40位SHA码* @param 待加密字符串* @return 返回40位SHA码*/public static String shaEncode(String inStr) throws Exception {MessageDigest sha = null;try {sha = MessageDigest.getInstance("SHA");} catch (Exception e) {System.out.println(e.toString());e.printStackTrace();return "";}byte[] byteArray = inStr.getBytes("UTF-8");byte[] md5Bytes = sha.digest(byteArray);StringBuffer hexValue = new StringBuffer();for (int i = 0; i < md5Bytes.length; i++) {int val = ((int) md5Bytes[i]) & 0xff;if (val < 16) { hexValue.append("0");}hexValue.append(Integer.toHexString(val));}return hexValue.toString();}/*** 测试主函数* @param args* @throws Exception*/public static void main(String args[]) throws Exception {String str = new String("amigoxiexiexingxing");System.out.println("原始:" + str);System.out.println("SHA后:" + shaEncode(str));}
}
输出如下:
原始:amigoxiexiexingxing
SHA后:04f79f496dd6bdab3439511606528a4ad9caac5e
2.3 MD5 与SHA-1对比
因为二者均由MD4导出,SHA-1和MD5彼此很相似。相应的,他们的强度和其他特性也是相似,但还有以下几点不同:
1)对强行攻击的安全性:最显著和最重要的区别是SHA-1摘要比MD5摘要长32 位。使用强行技术,产生任何一个报文使其摘要等于给定报摘要的难度对MD5是2128数量级的操作,而对SHA-1则是2160数量级的操作。这样,SHA-1对强行攻击有更大的强度。
2)对密码分析的安全性:由于MD5的设计,易受密码分析的攻击,SHA-1显得不易受这样的攻击。
3)速度:在相同的硬件上,SHA-1的运行速度比MD5慢。
3、对称加密算法DES、3DES和AES
3.1 对称加密算法
3.1.1 定义
对称加密算法是应用较早的加密算法,技术成熟。在对称加密算法中,数据发信方将明文(原始数据)和加密密钥(mi yue)一起经过特殊加密算法处理后,使其变成复杂的加密密文发送出去。收信方收到密文后,若想解读原文,则需要使用加密用过的密钥及相同算法的逆算法对密文进行解密,才能使其恢复成可读明文。在对称加密算法中,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密,这就要求解密方事先必须知道加密密钥。
3.1.2 优缺点
优点:算法公开、计算量小、加密速度快、加密效率高。
缺点:
(1)交易双方都使用同样钥匙,安全性得不到保证。
(2)每对用户每次使用对称加密算法时,都需要使用其他人不知道的惟一钥匙,这会使得发收信双方所拥有的钥匙数量呈几何级数增长,密钥管理成为用户的负担。对称加密算法在分布式网络系统上使用较为困难,主要是因为密钥管理困难,使用成本较高。
3.1.3 常用对称加密算法
基于“对称密钥”的加密算法主要有DES、3DES(TripleDES)、AES、RC2、RC4、RC5和Blowfish等。本文只介绍最常用的对称加密算法DES、3DES(TripleDES)和AES。
3.2 DES
3.2.1 概述
DES算法全称为Data Encryption Standard,即数据加密算法,它是IBM公司于1975年研究成功并公开发表的。DES算法的入口参数有三个:Key、Data、Mode。其中Key为8个字节共64位,是DES算法的工作密钥;Data也为8个字节64位,是要被加密或被解密的数据;Mode为DES的工作方式,有两种:加密或解密。
3.2.2 算法原理
DES算法把64位的明文输入块变为64位的密文输出块,它所使用的密钥也是64位,其算法主要分为两步:
(1)初始置换
其功能是把输入的64位数据块按位重新组合,并把输出分为L0、R0两部分,每部分各长32位,其置换规则为将输入的第58位换到第一位,第50位换到第2位……依此类推,最后一位是原来的第7位。L0、R0则是换位输出后的两部分,L0是输出的左32位,R0是右32位,例:设置换前的输入值为D1D2D3……D64,则经过初始置换后的结果为:L0=D58D50……D8;R0=D57D49……D7。
(2)逆置换
经过16次迭代运算后,得到L16、R16,将此作为输入,进行逆置换,逆置换正好是初始置换的逆运算,由此即得到密文输出。
3.2.3 五种分组模式
3.2.3.1 EBC模式
优点:
1.简单;
2.有利于并行计算;
3.误差不会被传送;
缺点:
1.不能隐藏明文的模式;
2.可能对明文进行主动攻击。
3.2.3.2 CBC模式
CBC模式又称为密码分组链接模式,示意图如下:
优点:
1.不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL、IPSec的标准。
缺点:
1、不利于并行计算;
2、误差传递;
3、需要初始化向量IV。
3.2.3.3 CFB模式
CFB模式又称为密码发反馈模式,示意图如下图所示
优点:
1、隐藏了明文模式;
2、分组密码转化为流模式;
3、可以及时加密传送小于分组的数据。
缺点:
1、不利于并行计算;
2、误差传送:一个明文单元损坏影响多个单元;
3、唯一的IV。
3.2.3.4 OFB模式
OFB模式又称输出反馈模式,示意图所下图所示:
优点:
1、隐藏了明文模式;
2、分组密码转化为流模式;
3、可以及时加密传送小于分组的数据。
缺点:
1、不利于并行计算;
2、对明文的主动攻击是可能的;
3、误差传送:一个明文单元损坏影响多个单元。
3.2.3.5 CTR模式
扩展,传送门
3.2.4 Java中的DES实现
DES加密算法(ECB、无填充)的Java实现如下所示:
生成秘钥:
//KeyGenerator,密钥生成器
KeyGenerator keyGen = KeyGenerator.getInstance("DES");//算法:DES,DESede,AES//初始化密钥生成器
keyGen.init(56); //各算法密钥长度不同,参见说明//生成密钥
SecretKey secretKey = keyGen.generateKey();//生产字节码数据
byte[] key = secretKey.getEncoded();
说明:
1.通过「KeyGenerator.getInstance(“DES”)」生成密钥,
2.参数为算法名称:分别对应DES、DESede(即3DES)、AES
3.每种算法密钥长度参数:DES(56),3DES(112,168),AES(192,256)
加、解密
//通过字节码数据key 恢复密钥
SecretKey secretKey = new SecretKeySpec(key, "DES");//Cipher完成加密/解密工作
Cipher cipher = Cipher.getInstance("DES");//根据密钥,对Cipher初始化,并选择加密还是解密
cipher.init(Cipher.ENCRYPT_MODE, secretKey);byte[] result = cipher.doFinal(data);
1.加密或解密都通过cipher.init()设置,参数:ENCRYPT_MODE/DECRYPT_MODE
2.加密或解密都通过cipher.doFinal() 执行,获得byte[]类型结果。
完整实例
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;public class DESUtil {/** 生成密钥*/public static byte[] initKey() throws Exception{KeyGenerator keyGen = KeyGenerator.getInstance("DES");keyGen.init(56);SecretKey secretKey = keyGen.generateKey();return secretKey.getEncoded();}/** DES 加密*/public static byte[] encrypt(byte[] data, byte[] key) throws Exception{SecretKey secretKey = new SecretKeySpec(key, "DES");Cipher cipher = Cipher.getInstance("DES");cipher.init(Cipher.ENCRYPT_MODE, secretKey);byte[] cipherBytes = cipher.doFinal(data);return cipherBytes;}/** DES 解密*/public static byte[] decrypt(byte[] data, byte[] key) throws Exception{SecretKey secretKey = new SecretKeySpec(key, "DES");Cipher cipher = Cipher.getInstance("DES");cipher.init(Cipher.DECRYPT_MODE, secretKey);byte[] plainBytes = cipher.doFinal(data);return plainBytes;}//Testpublic static void main(String[] args) throws Exception {byte[] desKey = DESUtil.initKey();System.out.println("DES KEY : " + BytesToHex.fromBytesToHex(desKey));byte[] desResult = DESUtil.encrypt(DATA.getBytes(), desKey);System.out.println(DATA + ">>>DES 加密结果>>>" + BytesToHex.fromBytesToHex(desResult));byte[] desPlain = DESUtil.decrypt(desResult, desKey);System.out.println(DATA + ">>>DES 解密结果>>>" + new String(desPlain));}
}
3.3 3DES
3.3.1 概述
3DES(或称为Triple DES)是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)块密码的通称。它相当于是对每个数据块应用三次DES加密算法。由于计算机运算能力的增强,原版DES密码的密钥长度变得容易被暴力破解;3DES即是设计用来提供一种相对简单的方法,即通过增加DES的密钥长度来避免类似的攻击,而不是设计一种全新的块密码算法。
3.3.2 算法原理
使用3条56位的密钥对 数据进行三次加密。3DES(即Triple DES)是DES向AES过渡的加密算法(1999年,NIST将3-DES指定为过渡的加密标准)。
其具体实现如下:设Ek()和Dk()代表DES算法的加密和解密过程,K代表DES算法使用的密钥,P代表明文,C代表密文,这样:
3DES加密过程为:C=Ek3(Dk2(Ek1§))
3DES解密过程为:P=Dk1(EK2(Dk3©))
3.3.3 Java中的3DES实现
3DES的在Java的实现与DES类似,如下代码为3DES加密算法、CBC模式、NoPadding填充方式的加密解密结果,参考代码如下所示:
核心代码:
加密部分的核心
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, deskey);
return cipher.doFinal(data);解密部分的核心
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE,deskey);
return cipher.doFinal(data);
完整示例
package DES;import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;public class ThreeDESUtil {//key 根据实际情况对应的修改private final byte[] keybyte="123456788765432112345678".getBytes(); //keybyte为加密密钥,长度为24字节private static final String Algorithm = "DESede"; //定义 加密算法,可用 DES,DESede,Blowfishprivate SecretKey deskey;//生成密钥public ThreeDESUtil(){deskey = new SecretKeySpec(keybyte, Algorithm);}//加密public byte[] encrypt(byte[] data){try {Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");cipher.init(Cipher.ENCRYPT_MODE, deskey);return cipher.doFinal(data);} catch (Exception ex) {//加密失败,打日志ex.printStackTrace();} return null;}//解密public byte[] decrypt(byte[] data){try {Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");cipher.init(Cipher.DECRYPT_MODE,deskey);return cipher.doFinal(data);} catch (Exception ex) {//解密失败,打日志ex.printStackTrace();} return null;}public static void main(String[] args) throws Exception {ThreeDESUtil des=new ThreeDESUtil();String req ="cryptology";String toreq = toHexString(req);System.err.println("十六进制报文=="+toreq);byte[] srcData=req.toString().getBytes("utf-8");byte[] encryptData=des.encrypt(srcData);System.out.println("密文:");if(encryptData!=null){for(int i=0;i<encryptData.length;i++){String hex=Integer.toHexString(encryptData[i]);if(hex.length()>1)System.out.print(hex.substring(hex.length()-2)+" ");elseSystem.out.print("0"+hex+" ");}}System.out.println("");System.out.println("明文:"+req);}// 转化字符串为十六进制编码public static String toHexString(String s) {String str = "";for (int i = 0; i < s.length(); i++) {int ch = (int) s.charAt(i);String s4 = Integer.toHexString(ch);str = str + s4;}return str;}
}
3.4 AES
package demo.security;import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Scanner;import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;/** AES对称加密和解密*/
public class SymmetricEncoder {/** 加密* 1.构造密钥生成器* 2.根据ecnodeRules规则初始化密钥生成器* 3.产生密钥* 4.创建和初始化密码器* 5.内容加密* 6.返回字符串*/public static String AESEncode(String encodeRules,String content){try {//1.构造密钥生成器,指定为AES算法,不区分大小写KeyGenerator keygen=KeyGenerator.getInstance("AES");//2.根据ecnodeRules规则初始化密钥生成器//生成一个128位的随机源,根据传入的字节数组keygen.init(128, new SecureRandom(encodeRules.getBytes()));//3.产生原始对称密钥SecretKey original_key=keygen.generateKey();//4.获得原始对称密钥的字节数组byte [] raw=original_key.getEncoded();//5.根据字节数组生成AES密钥SecretKey key=new SecretKeySpec(raw, "AES");//6.根据指定算法AES自成密码器Cipher cipher=Cipher.getInstance("AES");//7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二个参数为使用的KEYcipher.init(Cipher.ENCRYPT_MODE, key);//8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码byte [] byte_encode=content.getBytes("utf-8");//9.根据密码器的初始化方式--加密:将数据加密byte [] byte_AES=cipher.doFinal(byte_encode);//10.将加密后的数据转换为字符串//这里用Base64Encoder中会找不到包//解决办法://在项目的Build path中先移除JRE System Library,再添加库JRE System Library,重新编译后就一切正常了。String AES_encode=new String(new BASE64Encoder().encode(byte_AES));//11.将字符串返回return AES_encode;} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (NoSuchPaddingException e) {e.printStackTrace();} catch (InvalidKeyException e) {e.printStackTrace();} catch (IllegalBlockSizeException e) {e.printStackTrace();} catch (BadPaddingException e) {e.printStackTrace();} catch (UnsupportedEncodingException e) {e.printStackTrace();}//如果有错就返加nulllreturn null; }/** 解密* 解密过程:* 1.同加密1-4步* 2.将加密后的字符串反纺成byte[]数组* 3.将加密内容解密*/public static String AESDncode(String encodeRules,String content){try {//1.构造密钥生成器,指定为AES算法,不区分大小写KeyGenerator keygen=KeyGenerator.getInstance("AES");//2.根据ecnodeRules规则初始化密钥生成器//生成一个128位的随机源,根据传入的字节数组keygen.init(128, new SecureRandom(encodeRules.getBytes()));//3.产生原始对称密钥SecretKey original_key=keygen.generateKey();//4.获得原始对称密钥的字节数组byte [] raw=original_key.getEncoded();//5.根据字节数组生成AES密钥SecretKey key=new SecretKeySpec(raw, "AES");//6.根据指定算法AES自成密码器Cipher cipher=Cipher.getInstance("AES");//7.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二个参数为使用的KEYcipher.init(Cipher.DECRYPT_MODE, key);//8.将加密并编码后的内容解码成字节数组byte [] byte_content= new BASE64Decoder().decodeBuffer(content);/** 解密*/byte [] byte_decode=cipher.doFinal(byte_content);String AES_decode=new String(byte_decode,"utf-8");return AES_decode;} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (NoSuchPaddingException e) {e.printStackTrace();} catch (InvalidKeyException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (IllegalBlockSizeException e) {e.printStackTrace();} catch (BadPaddingException e) {e.printStackTrace();}//如果有错就返加nulllreturn null; }public static void main(String[] args) {SymmetricEncoder se=new SymmetricEncoder();Scanner scanner=new Scanner(System.in);/** 加密*/System.out.println("使用AES对称加密,请输入加密的规则");String encodeRules=scanner.next();System.out.println("请输入要加密的内容:");String content = scanner.next();System.out.println("根据输入的规则"+encodeRules+"加密后的密文是:"+se.AESEncode(encodeRules, content));/** 解密*/System.out.println("使用AES对称解密,请输入加密的规则:(须与加密相同)");encodeRules=scanner.next();System.out.println("请输入要解密的内容(密文):");content = scanner.next();System.out.println("根据输入的规则"+encodeRules+"解密后的明文是:"+se.AESDncode(encodeRules, content));}}
输出结果
使用AES对称加密,请输入加密的规则
使用AES对称加密
请输入要加密的内容:
使用AES对称加密
根据输入的规则使用AES对称加密加密后的密文是:Z0NwrNPHghgXHN0CqjLS58YCjhMcBfeR33RWs7Lw+AY=
使用AES对称解密,请输入加密的规则:(须与加密相同)
使用AES对称加密
请输入要解密的内容(密文):
Z0NwrNPHghgXHN0CqjLS58YCjhMcBfeR33RWs7Lw+AY=
根据输入的规则使用AES对称加密解密后的明文是:使用AES对称加密
4、非对称加密算法
4.1 RSA
RSA作为常用的非对称加密算法,用法基本就是首先生成公钥和私钥密钥对,然后用公钥加密 私钥解密或者用私钥加密 公钥解密。这里只写代码实现。
代码如下:
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;/*** RSA非对称加密算法工具类** @author lixk*/public class RSA {//非对称密钥算法private static final String KEY_ALGORITHM = "RSA";//密钥长度,在512到65536位之间,建议不要太长,否则速度很慢,生成的加密数据很长private static final int KEY_SIZE = 512;//字符编码private static final String CHARSET = "UTF-8";/*** 生成密钥对** @return KeyPair 密钥对*/public static KeyPair getKeyPair() throws Exception {return getKeyPair(null);}/*** 生成密钥对* @param password 生成密钥对的密码* @return* @throws Exception*/public static KeyPair getKeyPair(String password) throws Exception {//实例化密钥生成器KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);//初始化密钥生成器if(password == null){keyPairGenerator.initialize(KEY_SIZE);}else {SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");secureRandom.setSeed(password.getBytes(CHARSET));keyPairGenerator.initialize(KEY_SIZE, secureRandom);}//生成密钥对return keyPairGenerator.generateKeyPair();}/*** 取得私钥** @param keyPair 密钥对* @return byte[] 私钥*/public static byte[] getPrivateKeyBytes(KeyPair keyPair) {return keyPair.getPrivate().getEncoded();}/*** 取得Base64编码的私钥** @param keyPair 密钥对* @return String Base64编码的私钥*/public static String getPrivateKey(KeyPair keyPair) {return Base64.getEncoder().encodeToString(getPrivateKeyBytes(keyPair));}/*** 取得公钥** @param keyPair 密钥对* @return byte[] 公钥*/public static byte[] getPublicKeyBytes(KeyPair keyPair) {return keyPair.getPublic().getEncoded();}/*** 取得Base64编码的公钥** @param keyPair 密钥对* @return String Base64编码的公钥*/public static String getPublicKey(KeyPair keyPair) {return Base64.getEncoder().encodeToString(getPublicKeyBytes(keyPair));}/*** 私钥加密** @param data 待加密数据* @param privateKey 私钥字节数组* @return byte[] 加密数据*/public static byte[] encryptByPrivateKey(byte[] data, byte[] privateKey) throws Exception {//实例化密钥工厂KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);//生成私钥PrivateKey key = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey));//数据加密Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);cipher.init(Cipher.ENCRYPT_MODE, key);return cipher.doFinal(data);}/*** 私钥加密** @param data 待加密数据* @param privateKey Base64编码的私钥* @return String Base64编码的加密数据*/public static String encryptByPrivateKey(String data, String privateKey) throws Exception {byte[] key = Base64.getDecoder().decode(privateKey);return Base64.getEncoder().encodeToString(encryptByPrivateKey(data.getBytes(CHARSET), key));}/*** 公钥加密** @param data 待加密数据* @param publicKey 公钥字节数组* @return byte[] 加密数据*/public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {//实例化密钥工厂KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);//生成公钥PublicKey key = keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));//数据加密Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);cipher.init(Cipher.ENCRYPT_MODE, key);return cipher.doFinal(data);}/*** 公钥加密** @param data 待加密数据* @param publicKey Base64编码的公钥* @return String Base64编码的加密数据*/public static String encryptByPublicKey(String data, String publicKey) throws Exception {byte[] key = Base64.getDecoder().decode(publicKey);return Base64.getEncoder().encodeToString(encryptByPublicKey(data.getBytes(CHARSET), key));}/*** 私钥解密** @param data 待解密数据* @param privateKey 私钥字节数组* @return byte[] 解密数据*/public static byte[] decryptByPrivateKey(byte[] data, byte[] privateKey) throws Exception {//实例化密钥工厂KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);//生成私钥PrivateKey key = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey));//数据解密Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);cipher.init(Cipher.DECRYPT_MODE, key);return cipher.doFinal(data);}/*** 私钥解密** @param data Base64编码的待解密数据* @param privateKey Base64编码的私钥* @return String 解密数据*/public static String decryptByPrivateKey(String data, String privateKey) throws Exception {byte[] key = Base64.getDecoder().decode(privateKey);return new String(decryptByPrivateKey(Base64.getDecoder().decode(data), key), CHARSET);}/*** 公钥解密** @param data 待解密数据* @param publicKey 公钥字节数组* @return byte[] 解密数据*/public static byte[] decryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {//实例化密钥工厂KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);//产生公钥PublicKey key = keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));//数据解密Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);cipher.init(Cipher.DECRYPT_MODE, key);return cipher.doFinal(data);}/*** 公钥解密** @param data Base64编码的待解密数据* @param publicKey Base64编码的公钥* @return String 解密数据*/public static String decryptByPublicKey(String data, String publicKey) throws Exception {byte[] key = Base64.getDecoder().decode(publicKey);return new String(decryptByPublicKey(Base64.getDecoder().decode(data), key), CHARSET);}/*** 测试加解密方法** @param args* @throws Exception*/public static void main(String[] args) throws Exception {//生成密钥对,一般生成之后可以放到配置文件中KeyPair keyPair = RSA.getKeyPair();//公钥String publicKey = RSA.getPublicKey(keyPair);//私钥String privateKey = RSA.getPrivateKey(keyPair);System.out.println("公钥:\n" + publicKey);System.out.println("私钥:\n" + privateKey);String data = "RSA 加解密测试!";{System.out.println("\n===========私钥加密,公钥解密==============");String s1 = RSA.encryptByPrivateKey(data, privateKey);System.out.println("加密后的数据:" + s1);String s2 = RSA.decryptByPublicKey(s1, publicKey);System.out.println("解密后的数据:" + s2 + "\n\n");}{System.out.println("\n===========公钥加密,私钥解密==============");String s1 = RSA.encryptByPublicKey(data, publicKey);System.out.println("加密后的数据:" + s1);String s2 = RSA.decryptByPrivateKey(s1, privateKey);System.out.println("解密后的数据:" + s2 + "\n\n");}}
}
运行结果
公钥:
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIzqewW5l+TuYdFssqC+lRJ55jvay/DdNghVl22+k/jrF7yYjGefFgce37YbrEEZtVaRO8ddiBsJbIvFTadFsHECAwEAAQ==
私钥:
MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAjOp7BbmX5O5h0WyyoL6VEnnmO9rL8N02CFWXbb6T+OsXvJiMZ58WBx7fthusQRm1VpE7x12IGwlsi8VNp0WwcQIDAQABAkBIz7OnXqFsifwDIR4fTrpqJJBhJwmvuC1+GvO2of2FZPXq+mEfgixbRq18lUYDCuZ5qiSH4lczmO+03i6nFT6tAiEA2dnskcKHUNnQYKe14PaK5tL6mmm9xBv20jao/QgEdmMCIQCll514jjEW2beDJtcsLwpiUBX7hFqyrVthJLxeXxo8GwIhAKDDZAPQpSDyDAc33E1OQwTBzvOicsrExCku8xf/EaP3AiB0vbXTtxMek/EPckOCL8u3UdSdlErI6vSgQODX+14ofwIgbiV5yfTKwxMCVeuiksrUTKZ3n++4HBr3kgJChcsuPRA============私钥加密,公钥解密==============
加密后的数据:T2Iw9hwiE2Iss+BKpRT+mSPlrWp98MpQ/Xo5lx0Kdw8xXB+2X5NKvmnlDb3//E/oOeePOGJEUimv2CaRdOMwiw==
解密后的数据:RSA 加解密测试!===========公钥加密,私钥解密==============
加密后的数据:QNd0501gmNPn/ynSauRsfPIur+q6qUtKrlcjosZTjXwYm3d1VQ/IV9pLiNYHBPjL8Mbv7U/WHFo3rmQnNFXN3A==
解密后的数据:RSA 加解密测试!