博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
分组加密(ECB、CBC、CFB、OFB)实现(Java)
阅读量:3898 次
发布时间:2019-05-23

本文共 12436 字,大约阅读时间需要 41 分钟。

基本介绍

分组密码,也叫块加密(block cyphers),一次加密明文中的一个块。将明文按一定的位长分组,明文组经过加密运算得到密文组。将多个密文组合并成密文。密文组经过解密运算(加密运算的逆运算),还原成明文组。

序列密码,也叫流加密(stream cyphers),一次加密明文中的一个位。利用少量的密钥(制乱元素)通过某种复杂的运算(密码算法)产生大量的伪随机位流,用于对明文位流的加密。也可以用同样的密钥和密码算法及与加密相同的伪随机位流,用以还原明文位流。

四种分组加密模式

图片来源:

本文以分享代码为主,具体的逻辑原理请查看上述博客↑

ECB(Electronic Code Book)/电码本模式

ECB模式是最简单的一种,从图中就可以看出,就是利用一个密钥KEY分别对每个明文块进行加密,解密就是逆过程,同时可以发现每个明文块加密过程是相互独立的,可以并行,所以加密效率是最快的。

ECB加密过程ECB解密过程

CBC(Cipher Block Chaining)/密文分组链接方式

CBC模式与之前的ECB加密过程差不多,只不过每个明文块的加密密钥依赖于前一个明文加密后的结果,所以不能并行处理。

CBC加密过程
CBC解密过程

Cipher Feedback (CFB)/密文反馈模式

CFB模式就是对初始化向量IV进行加密再与明文异或来得到密文,再利用得到的密文作为下一个明文加密的密钥。

CFB加密过程

Output Feedback (OFB)/输出反馈模式

OFB模式与CFB模式的加密过程类似,不过OFB模式是对明文加密后再进行的异或操作。这个模式最神奇的地方是加密和解密的过程是一样的,你可以查看下面Java实现CFB的加解密函数,是一模一样的。

OFB加密过程

代码实现

代码中实现了对普通字节数组的加/解密,还实现了对图片加解密。对图片的加解密是利用直接内存实现的。处理效率高,可以减少一次从操作系统到JVM的IO

import javax.crypto.Cipher;import javax.crypto.SecretKey;import javax.crypto.SecretKeyFactory;import javax.crypto.spec.DESKeySpec;import javax.swing.filechooser.FileSystemView;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;import java.util.Arrays;public class PacketSecret {
private OperationMode mode;//要使用的加密运行模式 private SecretKey secretKey;//DES使用的密钥 private Cipher enCipher, deCipher;//加密/解密算法,这里选用DES private static final byte[] IV = {
1, 2, 3, 4, 5, 6, 7, 8}; private static byte count;//记录填充数 public PacketSecret() {
this(new ECB()); } public PacketSecret(OperationMode mode) {
this.mode = mode; initDES(); } public void setMode(OperationMode mode) {
this.mode = mode; } public OperationMode getMode() {
return mode; } //初始化密钥和加密算法 private void initDES() {
try {
//原始密钥,至少8个 byte[] rawKey = new byte[]{
1, 2, 3, 4, 5, 6, 7, 8}; DESKeySpec deskeySpec = new DESKeySpec(rawKey); secretKey = SecretKeyFactory.getInstance("DES").generateSecret(deskeySpec); enCipher = Cipher.getInstance("DES/ECB/NoPadding");//这里只使用普通的DES模式 deCipher = Cipher.getInstance("DES/ECB/NoPadding"); enCipher.init(Cipher.ENCRYPT_MODE, secretKey); deCipher.init(Cipher.DECRYPT_MODE, secretKey); } catch (Exception e) {
System.out.println("DES算法初始化失败"); e.printStackTrace(); } } //对分组数据预处理,以输入加密函数 private byte[] initPacket(byte[] data) {
if (data.length % 8 == 0) return data; //如果刚好64一组则直接进行加密,否则先填充 byte[] newData = new byte[((data.length >> 3) + 1) * 8]; System.arraycopy(data, 0, newData, 0, data.length);//默认填充0 count = (byte) (8 - data.length % 8);//记录填充了多少数据 return newData; } public byte[] packetEncrypt(byte[] data) {
byte[] packets = initPacket(data); try {
byte[] encryptData = mode.packetEncrypt(packets, enCipher); return encryptData; } catch (Exception e) {
e.printStackTrace(); } return null; } public byte[] packetDecrypt(byte[] packets) {
if (packets.length % 8 != 0) {
System.out.println("解密数据长度错误"); return null; } try {
byte[] rawData = mode.packetDecrypt(packets, deCipher, enCipher); if (count == 0) {
return rawData; } else {
return Arrays.copyOf(rawData, rawData.length - count); } } catch (Exception e) {
e.printStackTrace(); } return null; } private static void arraysXOR(byte[] src, byte[] des) {
if (src.length != des.length) {
System.out.println("数组长度不一致无法异或"); return; } for (int i = 0; i < src.length; i++) {
src[i] ^= des[i]; } } static class ECB implements OperationMode {
@Override public byte[] packetEncrypt(byte[] data, Cipher enCipher) throws Exception {
byte[] encryptData = new byte[data.length]; byte[] encryptBlock, block; //循环加密,每8字节一组 for (int i = 0; i < data.length; i += 8) {
block = Arrays.copyOfRange(data, i, i + 8);//获取分组 encryptBlock = enCipher.doFinal(block);//进行加密 //将数据拷贝到输出数组 System.arraycopy(encryptBlock, 0, encryptData, i, encryptBlock.length); } return encryptData; } @Override public byte[] packetDecrypt(byte[] data, Cipher deCipher, Cipher enCipher) throws Exception {
byte[] rawData = new byte[data.length]; byte[] decryptBlock, block; //循环加密,每8字节一组 for (int i = 0; i < data.length; i += 8) {
block = Arrays.copyOfRange(data, i, i + 8);//获取分组 decryptBlock = deCipher.doFinal(block);//进行解密 //将数据拷贝到输出数组 System.arraycopy(decryptBlock, 0, rawData, i, decryptBlock.length); } return rawData; } } static class CBC implements OperationMode {
@Override public byte[] packetEncrypt(byte[] data, Cipher enCipher) throws Exception {
byte[] encryptData = new byte[data.length]; byte[] encryptBlock, block; byte[] iv = Arrays.copyOf(IV, IV.length); //循环加密,每8字节一组 for (int i = 0; i < data.length; i += 8) {
block = Arrays.copyOfRange(data, i, i + 8);//获取分组 arraysXOR(block, iv);//保存异或结果到block encryptBlock = enCipher.doFinal(block);//对结果进行加密 iv = encryptBlock;//保存结果加密到iv //将数据拷贝到输出数组 System.arraycopy(encryptBlock, 0, encryptData, i, encryptBlock.length); } return encryptData; } @Override public byte[] packetDecrypt(byte[] data, Cipher deCipher, Cipher enCipher) throws Exception {
byte[] rawData = new byte[data.length]; byte[] decryptBlock, block; byte[] iv = Arrays.copyOf(IV, IV.length); //循环加密,每8字节一组 for (int i = 0; i < data.length; i += 8) {
block = Arrays.copyOfRange(data, i, i + 8);//获取分组 decryptBlock = deCipher.doFinal(block);//先进行解密 arraysXOR(decryptBlock, iv);//将异或结果保存到decryptBlock iv = block; //将数据拷贝到输出数组 System.arraycopy(decryptBlock, 0, rawData, i, decryptBlock.length); } return rawData; } } static class CFB implements OperationMode {
@Override public byte[] packetEncrypt(byte[] data, Cipher enCipher) throws Exception {
byte[] encryptData = new byte[data.length]; byte[] encryptBlock, block; byte[] iv = Arrays.copyOf(IV, IV.length); //循环加密,每8字节一组 for (int i = 0; i < data.length; i += 8) {
block = Arrays.copyOfRange(data, i, i + 8);//获取分组 encryptBlock = enCipher.doFinal(iv);//对IV进行加密 arraysXOR(encryptBlock, block);//保存异或结果到encryptBlock iv = encryptBlock;//保存密文结果到iv //将数据拷贝到输出数组 System.arraycopy(encryptBlock, 0, encryptData, i, encryptBlock.length); } return encryptData; } @Override public byte[] packetDecrypt(byte[] data, Cipher deCipher, Cipher enCipher) throws Exception {
byte[] rawData = new byte[data.length]; byte[] decryptBlock, block; byte[] iv = Arrays.copyOf(IV, IV.length); //循环加密,每8字节一组 for (int i = 0; i < data.length; i += 8) {
block = Arrays.copyOfRange(data, i, i + 8);//获取分组 iv = enCipher.doFinal(iv);//获取到加密后的IV arraysXOR(block, iv);//先将异或结果保存到Block decryptBlock = block; iv = Arrays.copyOfRange(data, i, i + 8); //将数据拷贝到输出数组 System.arraycopy(decryptBlock, 0, rawData, i, decryptBlock.length); } return rawData; } } static class OFB implements OperationMode {
@Override public byte[] packetEncrypt(byte[] data, Cipher enCipher) throws Exception {
byte[] encryptData = new byte[data.length]; byte[] encryptBlock, block; byte[] iv = Arrays.copyOf(IV, IV.length); //循环加密,每8字节一组 for (int i = 0; i < data.length; i += 8) {
block = Arrays.copyOfRange(data, i, i + 8);//获取分组 iv = enCipher.doFinal(iv);//对IV进行加密 encryptBlock = Arrays.copyOf(iv, iv.length); arraysXOR(encryptBlock, block);//保存异或结果到encryptBlock //将数据拷贝到输出数组 System.arraycopy(encryptBlock, 0, encryptData, i, encryptBlock.length); } return encryptData; } @Override public byte[] packetDecrypt(byte[] data, Cipher deCipher, Cipher enCipher) throws Exception {
//OFB模式下加解密过程是一致的 return packetEncrypt(data, enCipher); } } public static void printArrays(byte[] data) {
for (int i = 0; i < data.length; i++) {
System.out.print(String.format("0x%02x,", data[i])); } System.out.println(); } public static void imageTest(File infile, File outFile, Mode mode) {
PacketSecret secret = new PacketSecret(); secret.setMode(new ECB()); try (//自动关闭流 FileInputStream fis = new FileInputStream(infile); FileOutputStream fos = new FileOutputStream(outFile); ) {
FileChannel infileChannel = fis.getChannel(); FileChannel outfileChannel = fos.getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 5);//读入5k到缓冲区 byte[] secretText; while (infileChannel.read(byteBuffer) != -1) {
//读取到缓冲区 byte[] data = byteBuffer.array(); if (mode == Mode.Encrypt) {
secretText = secret.packetEncrypt(data); } else {
secretText = secret.packetDecrypt(data); } byteBuffer.rewind();//将指针指向头部 byteBuffer.put(secretText); byteBuffer.flip();//设置limit指针到position,准备读出 outfileChannel.write(byteBuffer); byteBuffer.clear(); } infileChannel.close(); outfileChannel.close(); } catch (Exception e) {
e.printStackTrace(); } } public static void simpleTest() {
byte[] text = new byte[]{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};//明文内容 PacketSecret secret = new PacketSecret(); secret.setMode(new ECB()); test(secret, text); secret.setMode(new CBC()); test(secret, text); secret.setMode(new CFB()); test(secret, text); secret.setMode(new OFB()); test(secret, text); } public static void test(PacketSecret secret, byte[] text) {
System.out.println("-------当前运行模式:" + secret.getMode().getClass().getSimpleName() + "-------"); byte[] pe = secret.packetEncrypt(text); System.out.println("----密文如下:----"); printArrays(pe); System.out.println("----解密后如下:----"); printArrays(secret.packetDecrypt(pe)); } public static void main(String[] args) {
// simpleTest();//字节数组测试 //在桌面放一张名为test.jpg的图片 System.out.println("在桌面放一张名为test.jpg的图片"); File file = new File(FileSystemView.getFileSystemView().getHomeDirectory(), "test.jpg"); //输出到桌面 File EncryptFile = new File(FileSystemView.getFileSystemView().getHomeDirectory(), "test-Encrypt.jpg"); File DecryptFile = new File(FileSystemView.getFileSystemView().getHomeDirectory(), "test-Decrypt.jpg"); System.out.println("加密图片文件路径:" + EncryptFile.getPath()); imageTest(file, EncryptFile, Mode.Encrypt);//输出加密图片 imageTest(EncryptFile, DecryptFile, Mode.Decrypt);//输出解密图片 } enum Mode {
Decrypt, Encrypt} //运作模式通用接口,采用"命令设计模式" public interface OperationMode {
byte[] packetEncrypt(byte[] data, Cipher cipher) throws Exception;//加密分组 byte[] packetDecrypt(byte[] data,Cipher deCipher,Cipher enCipher) throws Exception;//解密分组 }}

转载地址:http://dqfen.baihongyu.com/

你可能感兴趣的文章
算法之排序--希尔排序
查看>>
javascript深入浅出图解作用域链和闭包
查看>>
WebKit之Http请求
查看>>
WebKit之FrameWork层介绍
查看>>
WebKit之Http响应
查看>>
Webkit之支持WML
查看>>
Webkit之HTMLInput分析
查看>>
WebKit之UserAgent分析
查看>>
Webkit之HTMLToken处理
查看>>
Webkit之HTMLTokenizer分析
查看>>
Webkit之平台相关层
查看>>
Webkit之UI层布局
查看>>
WebKit之InlineBox绘制阶段顺序
查看>>
WebKit之图像显示分析流程
查看>>
WebKit之addToJavaScriptWindowObject()分析
查看>>
资源之收集列表整理
查看>>
JS之kindeditor的用法简介
查看>>
Linux之最简字符驱动的编码模型
查看>>
服务之Windows平台上搭建SVN服务
查看>>
Python之封装diff命令的项目比较命令(格式化diff输出结果)
查看>>