本文共 12436 字,大约阅读时间需要 41 分钟。
分组密码,也叫块加密(block cyphers
),一次加密明文中的一个块。将明文按一定的位长分组,明文组经过加密运算得到密文组。将多个密文组合并成密文。密文组经过解密运算(加密运算的逆运算),还原成明文组。
序列密码,也叫流加密(stream cyphers
),一次加密明文中的一个位。利用少量的密钥(制乱元素)通过某种复杂的运算(密码算法)产生大量的伪随机位流,用于对明文位流的加密。也可以用同样的密钥和密码算法及与加密相同的伪随机位流,用以还原明文位流。
图片来源:
本文以分享代码为主,具体的逻辑原理请查看上述博客↑ECB
模式是最简单的一种,从图中就可以看出,就是利用一个密钥KEY
分别对每个明文块进行加密,解密就是逆过程,同时可以发现每个明文块加密过程是相互独立的,可以并行,所以加密效率是最快的。
CBC
模式与之前的ECB加密过程差不多,只不过每个明文块的加密密钥依赖于前一个明文加密后的结果,所以不能并行处理。
CFB
模式就是对初始化向量IV
进行加密再与明文异或来得到密文,再利用得到的密文作为下一个明文加密的密钥。
OFB
模式与CFB
模式的加密过程类似,不过OFB
模式是对明文加密后再进行的异或操作。这个模式最神奇的地方是加密和解密的过程是一样的,你可以查看下面Java
实现CFB
的加解密函数,是一模一样的。
代码中实现了对普通字节数组的加/解密,还实现了对图片加解密。对图片的加解密是利用直接内存实现的。处理效率高,可以减少一次从操作系统到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/