代码里面都带有详细的注释,首先是三重DES的加密接口,每一步都是细分,步骤特别清晰(Des1.java):
package com.zmn.main;
/**
* @author zhuangmingnan
* DES的接口
*/
public interface Des1 {
// 需要注意的是CFB、OFB模式下是将分组密码转换成流密码,所以没有填充字节的行为,每次只加密一个字符即8个字节
// ECB、CBC模式如果明文长度l%8 != 0 ,那么系统默认会在最后一个分组补齐64bit,补齐方法是补0
static final int ECB_MODE = 0; // 电码本ECB
static final int CBC_MODE = 1; // 密码分组链接模式(CBC)
static final int CFB_MODE = 2; // 密码反馈模式(CFB)
static final int OFB_MODE = 3; // 输出反馈模式(OFB)
// 三重DES加密,指定工作模式,工作在CBC,CFB,OFB三种模式下就需要设置Vi的值
String TripleDES_Encrypt_GivenMode(String message, String key1, String key2,int mode, String Vi);
// 三重DES解密,指定工作模式,工作在CBC,CFB,OFB三种模式下就需要设置Vi的值
String TripleDES_deciphering_GivenMode(String message, String key1, String key2,int mode, String Vi);
// 将二进制表示的String字符串转成byte[]数组
String BinaryString2String(String str);
// 注释掉的内容均为3DES的实现类的内部调用的私有方法声明,不暴露给用户使用,只提供三重des的加密解密和通用的转化方法给用户
/*
// 接收8个字符的字符串,返回64个字符的该字符串表示
String get64bitMessage(String message8Character);
// setp1: 初始置换IP 接收64比特的明文数据,返回初始置换IP后的64比特数据
String initialPermutate(String byte64Message);
// 选择扩展运算E,输入32bit数据,返回48bit的数据
String selectiveExtensions(String byte32Message);
// 密钥编排算法,输入64bit密钥,产生16组48bit子密钥,下标0-15
String[] produceSubSecretKey(String secret64Key);
// 循环左移step位,位数不限
String LS(String message, int step);
// 异或运算, 两个操作数位数必须相同
String exorOperation(String binaryOper1, String binaryOper2);
// 选择压缩运算S 输入48bit数据,输出32bit数据
String selectiveCompressionS(String message48);
// 置换运算P
String substituteOperationP(String message32);
// 交换左右32比特
String changeLR(String message64);
// 初始逆置换IP-1
String initialInversePermutation(String message64);
// 在密钥的控制下进行16轮迭代
String iterate16Round(String byte64Message, String[] secretKey);
// F(轮函数)
String roundFunctionF(String message32, String Currentkey);
// DES加密
String encrypt(String message, String key);
// DES解密
String deciphering(String message, String key);
// 三重DES加密
String TripleDES_Encrypt(String message, String key1, String key2);
// 三重DES解密
String TripleDES_deciphering(String message, String key1, String key2);
*/
}
接下来是对于上面接口的实现,上面接口中注释的方法都在Des1Impl.java中实现,对外只提供加密、解密和公共的转换方法:
package com.zmn.main;
import com.zmn.table.Table;
import com.zmn.test.TestMethod;
public class Des1Impl implements Des1 {
/* 接收8个字符的字符串,返回64个字符的该字符串表示 */
private String get64bitMessage(String message8Character) {
StringBuffer sb = new StringBuffer();
for(char c : message8Character.toCharArray()){
String binaryMessage = Integer.toBinaryString(c);
int length = binaryMessage.length();
while(length < 8){
sb.append("0");
length ++;
}
sb.append(binaryMessage);
}
return sb.toString();
}
/* 初始置换IP */
private String initialPermutate(String byte64Message) {
char[] charArray = byte64Message.toCharArray();
for(int i = 0; i < byte64Message.length(); i++){
charArray[i] = byte64Message.charAt(Table.initialPermutationIP[i] - 1);
}
return new String(charArray);
}
// 选择扩展运算E,输入32bit数据,返回48bit的数据
private String selectiveExtensions(String byte32Message) {
StringBuffer sb = new StringBuffer();
for(int i = 0; i < 48; i ++){
sb.append(byte32Message.charAt(Table.selectiveExtensionsE[i] - 1));
}
return sb.toString();
}
// 密钥编排算法,输入64bit密钥,产生16组48bit子密钥K,下标0-15
private String[] produceSubSecretKey(String secret64Key) {
StringBuffer sb = new StringBuffer();
for(int i = 0; i < Table.substitutionSelectionPC1.length; i++){
sb.append(secret64Key.charAt(Table.substitutionSelectionPC1[i] - 1));
}
String CD = sb.toString(); // 密钥选择算法1(PC-1)之后产生的CD
String C = CD.substring(0, CD.length() / 2); // 左半部分
String D = CD.substring(CD.length() / 2); // 右半部分
String[] K = new String[16]; // 16组子密钥,K0-K15
sb.delete(0, sb.length()); // 清空数据,复用sb对象
for(int i = 0; i < 16; i++){
int moveStep = 2;
if(i == 1 - 1 || i == 2 - 1 || i == 9 - 1 || i == 16 -1)
moveStep = 1;
C = LS(C,moveStep);
D = LS(D,moveStep);
CD = C + D;
for(int j = 0; j < Table.substitutionSelectionPC2.length; j++){
sb.append( CD.charAt( Table.substitutionSelectionPC2[j] - 1 ) );
}
K[i] = sb.toString();
sb.delete(0, sb.length()); // 清空数据,复用sb对象
}
return K;
}
// 循环左移step位,位数不限
private String LS(String message, int step) {
StringBuffer sb = new StringBuffer();
for(int i = 0; i < message.length(); i++){
sb.append( message.charAt( (i+step)%message.length() ) );
}
return sb.toString();
}
// 异或运算, 两个操作数位数必须相同,返回的数是二进制表示
private String exorOperation(String binaryOper1, String binaryOper2) {
StringBuffer sb = new StringBuffer();
for(int i = 0; i < binaryOper1.length(); i++){
sb.append( ( (binaryOper1.charAt(i) - '0') + ( binaryOper2.charAt(i) - '0') ) % 2 ); // 异或的操作等同于模2加
}
return sb.toString();
}
// 选择压缩运算S 输入48bit数据,输出32bit数据
private String selectiveCompressionS(String message48) {
StringBuffer sb = new StringBuffer();
String temp = null;
int row = 0;
int col = 0;
for(int i = 0; i < Table.SBox.length; i++){
row = (message48.charAt(i * 6) - '0')*2 + (message48.charAt(i * 6 + 5) - '0');
col = (message48.charAt(i * 6 + 1) - '0')*8 + (message48.charAt(i * 6 + 2) - '0')*4
+ (message48.charAt(i * 6 + 3) - '0')*2 + (message48.charAt(i * 6 + 4) - '0');
temp = Integer.toBinaryString(Table.SBox[i][row*16+col]);
while(temp.length() < 4){
temp = "0"+temp;
}
sb.append(temp); // 将从S盒取得的数字转换成四位二进制数,后存入sb对象
}
return sb.toString();
}
// 置换运算P
private String substituteOperationP(String message32) {
StringBuffer sb = new StringBuffer();
for(int i = 0; i < Table.substitutionP.length; i++){
sb.append( message32.charAt(Table.substitutionP[i]-1) );
}
return sb.toString();
}
// 交换左右32比特
private String changeLR(String message64) {
StringBuffer sb = new StringBuffer();
sb.append( message64.substring(message64.length()/2) );
sb.append( message64.substring(0, message64.length()/2) );
return sb.toString();
}
// 初始逆置换IP-1
private String initialInversePermutation(String message64) {
StringBuffer sb = new StringBuffer();
for(int i = 0; i < Table.initialInversePermutationIP.length; i ++){
sb.append( message64.charAt(Table.initialInversePermutationIP[i]-1) );
}
return sb.toString();
}
// 在密钥控制下16轮迭代
private String iterate16Round(String byte64Message, String[] secretKey) {
String left32Before = byte64Message.substring(0, byte64Message.length()/2);
String right32Before = byte64Message.substring(byte64Message.length()/2);
String left32 = left32Before;
String right32 = right32Before;
for(int i = 0; i < 16; i++){
right32 = exorOperation(left32Before, roundFunctionF(right32Before, secretKey[i]));
left32 = right32Before;
left32Before = left32;
right32Before = right32;
}
return left32 + right32;
}
// F·轮函数
private String roundFunctionF(String message32, String Currentkey) {
String outputMessage = selectiveExtensions(message32); // 选择扩展运算E
outputMessage = exorOperation(outputMessage, Currentkey); // 选择扩展运算E输出的48bit数据和当前子密钥异或
outputMessage = selectiveCompressionS(outputMessage); // 选择压缩运算S
outputMessage = substituteOperationP(outputMessage); // 置换运算P
return outputMessage;
}
// DES加密
private String encrypt(String message, String key) {
String message64 = null;
StringBuffer sb = new StringBuffer();
int i = 0;
String key64 = get64bitMessage(key);
String[] subKey = produceSubSecretKey(key64);
for(i = 0; (i+7) < message.length(); i+=8){
message64 = get64bitMessage( message.substring(i, i+8) );
message64 = initialPermutate(message64);
message64 = iterate16Round(message64, subKey);
message64 = changeLR(message64);
message64 = initialInversePermutation(message64);
sb.append(message64);
}
if(i == message.length())
return BinaryString2String(sb.toString());
String messageLT8Character = message.substring(i);
for(i = (8 - (message.length() - i) ); i > 0; i--){
messageLT8Character += "0";
}
message64 = get64bitMessage( messageLT8Character );
message64 = initialPermutate(message64);
message64 = iterate16Round(message64, subKey);
message64 = changeLR(message64);
message64 = initialInversePermutation(message64);
sb.append(message64);
return BinaryString2String(sb.toString());
}
// DES解密
private String deciphering(String message, String key) {
String message64 = null;
StringBuffer sb = new StringBuffer();
int i = 0;
String key64 = get64bitMessage(key);
String[] subKey = produceSubSecretKey(key64);
for(i = 0; i < subKey.length/2; i++){
String temp = subKey[i];
subKey[i] = subKey[subKey.length - i - 1];
subKey[subKey.length - i - 1] = temp;
}
for(i = 0; (i+7) < message.length(); i+=8){
message64 = get64bitMessage( message.substring(i, i+8) );
message64 = initialPermutate(message64);
message64 = iterate16Round(message64, subKey);
message64 = changeLR(message64);
message64 = initialInversePermutation(message64);
sb.append(message64);
}
if(i == message.length())
return BinaryString2String(sb.toString());
TestMethod.printEvery8BitSplitBySpace(sb.toString());
String messageLT8Character = message.substring(i);
for(i = (8 - (message.length() - i) ); i > 0; i--){
messageLT8Character += "0";
}
message64 = get64bitMessage( messageLT8Character );
message64 = initialPermutate(message64);
message64 = iterate16Round(message64, subKey);
message64 = changeLR(message64);
message64 = initialInversePermutation(message64);
sb.append(message64);
return BinaryString2String(sb.toString());
}
// 三重DES加密
private String TripleDES_Encrypt(String message, String key1, String key2) {
String temp = encrypt(message, key1);
temp = deciphering(temp, key2);
temp = encrypt(temp, key1);
return temp;
}
// 三重DES解密
private String TripleDES_deciphering(String message, String key1, String key2) {
String temp = deciphering(message, key1);
temp = encrypt(temp, key2);
temp = deciphering(temp, key1);
return temp;
}
// 将二进制表示的String字符串转成对应的String字符,将8位二进制转成1位字符
@Override
public String BinaryString2String(String str) {
String binary8String = null;
StringBuffer sb = new StringBuffer();
int asciiNumber = 0;
for(int i = 0; i < str.length()/8 || (str.length() == 8 && i == 0); i++){
binary8String = str.substring(i*8, i*8+8);
asciiNumber = 0;
for(int j = 0; j < binary8String.length(); j++){
asciiNumber += (binary8String.charAt(j)-'0')*Math.pow(2, (7-j));
}
sb.append((char)asciiNumber);
}
return sb.toString();
}
// 三重DES加密,指定工作模式
@Override
public String TripleDES_Encrypt_GivenMode(String message, String key1, String key2,int mode, String Vi) {
StringBuffer sb = new StringBuffer();
String messageLeft = null;
String beforeRoundC = Vi;
String bit8Message = null;
String beforeRound64BitMessage = null;
String temp = null;
String beforeRoundOutput8Bit = null;
int i = 0;
switch(mode){
case ECB_MODE:
return TripleDES_Encrypt(message, key1, key2);
case CBC_MODE:
if(Vi == null)
return "Vi is NULL";
for(i = 0; i < message.length() / 8; i++){
String message8Character = message.substring(i*8, i*8+8);
message8Character = BinaryString2String(exorOperation(get64bitMessage(message8Character), get64bitMessage(beforeRoundC)));
String outputEncryptMessage = TripleDES_Encrypt(message8Character, key1, key2);
sb.append( outputEncryptMessage );
beforeRoundC = outputEncryptMessage;
}
if(message.length() % 8 != 0){
messageLeft = message.substring(i*8);
int needAddLength = 8 - (message.length() % 8);
while(needAddLength-- > 0)
messageLeft += '0';
messageLeft = BinaryString2String(exorOperation(get64bitMessage(messageLeft), get64bitMessage(beforeRoundC)));
sb.append( TripleDES_Encrypt(messageLeft, key1, key2) );
}
return sb.toString();
case CFB_MODE:
if(beforeRoundC == null)
return "Vi is NULL";
beforeRound64BitMessage = get64bitMessage(Vi);
bit8Message = get64bitMessage(TripleDES_Encrypt(BinaryString2String(beforeRound64BitMessage), key1, key2)).substring(0, 8);
temp = Integer.toBinaryString(message.charAt(0));
while(temp.length() < 8)
temp = '0' + temp ;
beforeRoundC = BinaryString2String(exorOperation(temp, bit8Message));
sb.append(beforeRoundC);
for(i = 1; i < message.length(); i++){
beforeRound64BitMessage = LS(beforeRound64BitMessage, 8).substring(0, 64-8);
temp = Integer.toBinaryString(beforeRoundC.charAt(0));
while(temp.length() < 8)
temp = '0' + temp ;
beforeRound64BitMessage += temp;
bit8Message = get64bitMessage( TripleDES_Encrypt(BinaryString2String(beforeRound64BitMessage), key1, key2) ).substring(0, 8);
temp = Integer.toBinaryString(message.charAt(i));
while(temp.length() < 8)
temp = '0' + temp;
beforeRoundC = BinaryString2String(exorOperation(temp, bit8Message));
sb.append(beforeRoundC);
}
return sb.toString();
case OFB_MODE:
if(beforeRoundC == null)
return "Vi is NULL";
beforeRound64BitMessage = get64bitMessage(Vi);
bit8Message = get64bitMessage(TripleDES_Encrypt(BinaryString2String(beforeRound64BitMessage), key1, key2)).substring(0, 8);
temp = Integer.toBinaryString(message.charAt(0));
while(temp.length() < 8)
temp = '0' + temp ;
beforeRoundOutput8Bit = bit8Message;
sb.append( BinaryString2String(exorOperation(temp, bit8Message)) );
for(i = 1; i < message.length(); i++){
beforeRound64BitMessage = LS(beforeRound64BitMessage, 8).substring(0, 64-8);
beforeRound64BitMessage += beforeRoundOutput8Bit;
bit8Message = get64bitMessage( TripleDES_Encrypt(BinaryString2String(beforeRound64BitMessage), key1, key2) ).substring(0, 8);
temp = Integer.toBinaryString(message.charAt(i));
while(temp.length() < 8)
temp = '0' + temp;
beforeRoundOutput8Bit = bit8Message ;
sb.append(BinaryString2String(exorOperation(temp, bit8Message)));
}
return sb.toString();
}// case statement end===
return null;
}
// 三重DES解密,指定工作模式
@Override
public String TripleDES_deciphering_GivenMode(String message, String key1, String key2,int mode, String Vi) {
StringBuffer sb = new StringBuffer();
String messageLeft = null;
String beforeRoundC = Vi;
String bit8Message = null;
String beforeRound64BitMessage = null;
String beforeRoundOutput8Bit = null;
String temp = null;
int i = 0;
switch(mode){
case ECB_MODE:
return TripleDES_deciphering(message, key1, key2);
case CBC_MODE:
if(beforeRoundC == null)
return "Vi is NULL";
for(i = 0; i < message.length() / 8; i++){
String message8Character = message.substring(i*8, i*8+8);
String outputDecipheringMessage = TripleDES_deciphering(message8Character, key1, key2);
message8Character = BinaryString2String(exorOperation(get64bitMessage(outputDecipheringMessage), get64bitMessage(beforeRoundC)));
beforeRoundC = message.substring(i*8, i*8+8);
sb.append(message8Character);
}
if(message.length() % 8 != 0){
messageLeft = message.substring(i*8);
int needAddLength = 8 - (message.length() % 8);
while(needAddLength-- > 0)
messageLeft += '0';
messageLeft = BinaryString2String(exorOperation(get64bitMessage(messageLeft), get64bitMessage(beforeRoundC)));
sb.append( TripleDES_deciphering(messageLeft, key1, key2) );
}
return sb.toString();
case CFB_MODE:
if(beforeRoundC == null)
return "Vi is NULL";
beforeRound64BitMessage = get64bitMessage(Vi);
bit8Message = get64bitMessage(TripleDES_Encrypt(BinaryString2String(beforeRound64BitMessage), key1, key2)).substring(0, 8);
temp = Integer.toBinaryString(message.charAt(0));
while(temp.length() < 8)
temp = '0' + temp ;
beforeRoundC = message.charAt(0)+"";
sb.append( BinaryString2String(exorOperation(temp, bit8Message)) );
for(i = 1; i < message.length(); i++){
beforeRound64BitMessage = LS(beforeRound64BitMessage, 8).substring(0, 64-8);
temp = Integer.toBinaryString(beforeRoundC.charAt(0));
while(temp.length() < 8)
temp = '0' + temp ;
beforeRound64BitMessage += temp;
bit8Message = get64bitMessage( TripleDES_Encrypt(BinaryString2String(beforeRound64BitMessage), key1, key2) ).substring(0, 8);
temp = Integer.toBinaryString(message.charAt(i));
while(temp.length() < 8)
temp = '0' + temp;
beforeRoundC = message.charAt(i)+"";
sb.append( BinaryString2String(exorOperation(temp, bit8Message)));
}
return sb.toString();
case OFB_MODE:
if(beforeRoundC == null)
return "Vi is NULL";
beforeRound64BitMessage = get64bitMessage(Vi);
bit8Message = get64bitMessage(TripleDES_Encrypt(BinaryString2String(beforeRound64BitMessage), key1, key2)).substring(0, 8);
temp = Integer.toBinaryString(message.charAt(0));
while(temp.length() < 8)
temp = '0' + temp ;
beforeRoundOutput8Bit = bit8Message;
sb.append( BinaryString2String(exorOperation(temp, bit8Message)) );
for(i = 1; i < message.length(); i++){
beforeRound64BitMessage = LS(beforeRound64BitMessage, 8).substring(0, 64-8);
beforeRound64BitMessage += beforeRoundOutput8Bit;
bit8Message = get64bitMessage( TripleDES_Encrypt(BinaryString2String(beforeRound64BitMessage), key1, key2) ).substring(0, 8);
temp = Integer.toBinaryString(message.charAt(i));
while(temp.length() < 8)
temp = '0' + temp;
beforeRoundOutput8Bit = bit8Message ;
sb.append(BinaryString2String(exorOperation(temp, bit8Message)));
}
return sb.toString();
}
return null;
}
}
为了方便调试调用,这里用JavaSwing提供了一个简单的友好的用户图形化界面,方便使用,初始选择加密类型的界面类(GraphicsUserInterface.java):
package com.zmn.main;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;
public class GraphicsUserInterface extends JFrame{
private static final long serialVersionUID = 8999747999368337069L;
static int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;
static int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
static int frmWidth = 300;
static int frmHeight = 200;
public GraphicsUserInterface(){
JTextField jtf ,message;
JButton encryptStringBtn, encryptFileBtn;
setLayout(null);
jtf = new JTextField("请选择加密类型:");
jtf.setBounds(20, 10, 140, 20);
jtf.setEditable(false);
jtf.setBorder(null);
encryptStringBtn = new JButton("加密字符串");
encryptStringBtn.setBounds(frmWidth/2 - 70, 40, 140, 30);
encryptStringBtn.addActionListener(new ButtonClickListen());
encryptFileBtn = new JButton("加密文件");
encryptFileBtn.setBounds(frmWidth/2 - 70, 80, 140, 30);
encryptFileBtn.addActionListener(new ButtonClickListen());
message = new JTextField("加密文件仅支持文本类型!");
message.setBounds(frmWidth/2 - 70, 120, 140, 20);
message.setEditable(false);
message.setBorder(null);
add(jtf);
add(encryptStringBtn);
add(encryptFileBtn);
add(message);
}
public static void main(String[] args) {
GraphicsUserInterface jfrm = new GraphicsUserInterface();
jfrm.setTitle("三重DES加密软件");
jfrm.setBounds(screenWidth/2 - frmWidth/2 , screenHeight/2 - frmHeight/2, frmWidth, frmHeight);
jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jfrm.setVisible(true);
}
}
class ButtonClickListen implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
switch (((JButton)e.getSource()).getText()){
case "加密字符串":
StringEncrptFrame stringEncrptFrame = new StringEncrptFrame();
stringEncrptFrame.setVisible(true);
break;
case "加密文件":
FileEncrptFrame fileEncrptFrame = new FileEncrptFrame();
fileEncrptFrame.setVisible(true);
break;
}
}
}
接下来分别是对两种加密类型的界面实现和加密实现:
(字符串加密界面)StringEncrptFrame.java
package com.zmn.main;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
public class StringEncrptFrame extends JFrame{
private int currentHeight = 10; // 绝对布局高度控制变量,每次添加一行组件之后该变量要增加添加最大组件的高度+10(两行的间隔)
private JTextArea EmessageArea,DmessageArea;
private JTextField key1Input, key2Input, ViInput;
private JComboBox<String> workModeCbx;
private JButton encryptBtn, decipheringBtn;
private Des1 des = new Des1Impl(); // 加密解密用到的对象
public StringEncrptFrame(){
int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width; // 屏幕宽度
int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height; // 屏幕高度
int frmWidth = 400; // 窗口宽度
int frmHeight = 450; // 窗口高度
setBounds((screenWidth - frmWidth)/2, (screenHeight - frmHeight)/2, frmWidth, frmHeight);
setTitle("字符串加密");
setLayout(null);
// 初始化各个组件,初始化界面
initElement();
//添加动态监听事件
addElementListener();
}
// 初始化各个组件,初始化界面
private void initElement(){
JTextField EmessageField,DmessageField, key1Field, key2Field, workModeField, ViField;
EmessageField = new JTextField("密文:");
DmessageField = new JTextField("明文:");
key1Field = new JTextField("密钥1(8字符):");
key2Field = new JTextField("密钥2(8字符):");
ViField = new JTextField("初始向量Vi:");
workModeField = new JTextField("工作模式:");
EmessageArea = new JTextArea("input Emessage Here", 5, 30);
DmessageArea = new JTextArea("input Dmessage here", 5,30);
key1Input = new JTextField();
key2Input = new JTextField();
ViInput = new JTextField();
encryptBtn = new JButton("加密");
decipheringBtn = new JButton("解密");
workModeCbx = new JComboBox<>();
workModeCbx.addItem("ECB");
workModeCbx.addItem("CBC");
workModeCbx.addItem("CFB");
workModeCbx.addItem("OFB");
DmessageField.setEditable(false);
DmessageField.setBorder(null);
DmessageField.setBounds(20,currentHeight,80,30);
DmessageArea.setBounds(100,currentHeight,8*30, 30*10);
DmessageArea.setLineWrap(true); // 自动换行
DmessageArea.setWrapStyleWord(true); // 断行不断字
JScrollPane dmessageAreaScroll = new JScrollPane(DmessageArea);
dmessageAreaScroll.setBounds(100,currentHeight,8*30, 30*3);
dmessageAreaScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
currentHeight += dmessageAreaScroll.getHeight()+10;
key1Field.setEditable(false);
key1Field.setBorder(null);
key1Field.setBounds(5,currentHeight,90,30);
key1Input.setBounds(100,currentHeight,240,30);
currentHeight += key1Input.getHeight()+10;
key2Field.setEditable(false);
key2Field.setBorder(null);
key2Field.setBounds(5,currentHeight,90,30);
key2Input.setBounds(100,currentHeight,240,30);
currentHeight += key2Input.getHeight()+10;
ViField.setEditable(false);
ViField.setBorder(null);
ViField.setBounds(5,currentHeight,80,30);
ViField.setEditable(false);
ViInput.setBounds(100,currentHeight,240,30);
ViInput.setEditable(false);
currentHeight += ViInput.getHeight()+10;
EmessageField.setEditable(false);
EmessageField.setBorder(null);
EmessageField.setBounds(20,currentHeight,80,30);
EmessageArea.setBounds(100,currentHeight,8*30, 30*10);
EmessageArea.setLineWrap(true); // 自动换行
EmessageArea.setWrapStyleWord(true); // 断行不断字
JScrollPane emessageAreaScroll = new JScrollPane(EmessageArea);
emessageAreaScroll.setBounds(100,currentHeight,8*30, 30*3);
emessageAreaScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
currentHeight += emessageAreaScroll.getHeight()+10;
workModeField.setEditable(false);
workModeField.setBorder(null);
workModeField.setBounds(20,currentHeight,80,30);
workModeCbx.setBounds(150,currentHeight,100,30);
currentHeight += workModeCbx.getHeight()+10;
encryptBtn.setBounds(100,currentHeight,80,30);
decipheringBtn.setBounds(210,currentHeight,80,30);
currentHeight += decipheringBtn.getHeight()+10;
add(DmessageField);add(dmessageAreaScroll);
add(key1Field);add(key1Input);
add(key2Field);add(key2Input);
add(ViField);add(ViInput);
add(EmessageField);add(emessageAreaScroll);
add(workModeField); add(workModeCbx);
add(encryptBtn);add(decipheringBtn);
}
// 增加组件的事件监听者
private void addElementListener(){
encryptBtn.addActionListener(new BtnActionListener());
decipheringBtn.addActionListener(new BtnActionListener());
workModeCbx.addItemListener(new comBoxChangeListener());
}
// 加密/解密 按钮事件监听者类
class BtnActionListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
String Emessage = EmessageArea.getText();
String Dmessage = DmessageArea.getText();
String key1 = key1Input.getText();
String key2 = key1Input.getText();
// 电码本ECB ECB_MODE = 0;
// 密码分组链接模式(CBC) CBC_MODE = 1;
// 密码反馈模式(CFB) CFB_MODE = 2;
// 输出反馈模式(OFB) OFB_MODE = 3;
int mode = workModeCbx.getSelectedIndex();
String Vi = ViInput.getText();
if(e.getSource() == encryptBtn){
if(!judgeInputNotNull(0, mode))
return ;
System.out.println("Dmessage:" + Dmessage);
Emessage = des.TripleDES_Encrypt_GivenMode(Dmessage, key1, key2, mode, Vi);
EmessageArea.setText(Emessage);
System.out.println("Emessage:" + Emessage);
}else if(e.getSource() == decipheringBtn){
if(!judgeInputNotNull(1, mode))
return ;
System.out.println("Emessage:" + Emessage);
Dmessage = des.TripleDES_deciphering_GivenMode(Emessage, key1, key2, mode, Vi);
DmessageArea.setText(Dmessage);
System.out.println("Dmessage:" + Dmessage);
}
}
}
// 下拉列表事件监听者类
class comBoxChangeListener implements ItemListener{
@Override
public void itemStateChanged(ItemEvent e) {
if(e.getSource() == workModeCbx && e.getStateChange() == ItemEvent.SELECTED){
if(workModeCbx.getSelectedIndex() > 1){
ViInput.setEditable(true);
}else{
ViInput.setEditable(false);
}
}
}
}
/**
* 判断加密/解密中,对应的信息是否不为空/正确,给出友好的用户提示信息
* @param EorD 加密还是解密,0表示加密,1表示解密
* @param mode 工作模式,由此来判断是否需要Vi作为初始向量 0表示ECB,1表示CBC,2表示CFB,3表示OFB
* @return boolean 验证通过返回true,不通过返回false
*/
private boolean judgeInputNotNull(int EorD, int mode){
if(EorD == 0) // 加密还是解密,0表示加密,1表示解密
//明文数据没有输入
if("".equals(DmessageArea.getText()) || DmessageArea.getText() == null){
JOptionPane.showMessageDialog(null, "请输入明文数据!", "字段空缺",JOptionPane.INFORMATION_MESSAGE);
return false;
}
else
//密文数据没有输入
if("".equals(EmessageArea.getText()) || EmessageArea.getText() == null){
JOptionPane.showMessageDialog(null, "请输入密文数据!", "字段空缺",JOptionPane.INFORMATION_MESSAGE);
return false;
}
//key1没有输入
if("".equals(key1Input.getText()) || key1Input.getText() == null) {
JOptionPane.showMessageDialog(null, "请输入key1数据!", "字段空缺", JOptionPane.INFORMATION_MESSAGE);
return false;
}
else
// key1的长度要求是8个字符,此处子密钥要求64bit
if(key1Input.getText().length() != 8) {
JOptionPane.showMessageDialog(null, "key1必须是8个字符!", "字段格式错误", JOptionPane.INFORMATION_MESSAGE);
return false;
}
//key2没有输入
if("".equals(key2Input.getText()) || key2Input.getText() == null){
JOptionPane.showMessageDialog(null, "请输入key2数据!", "字段空缺", JOptionPane.INFORMATION_MESSAGE);
return false;
}
else
// key1的长度要求是8个字符,此处子密钥要求64bit
if(key2Input.getText().length() != 8) {
JOptionPane.showMessageDialog(null, "key2必须是8个字符!", "字段格式错误", JOptionPane.INFORMATION_MESSAGE);
return false;
}
if(mode >= Des1.CFB_MODE) // CFB和OFB都需要Vi作为初始向量
// Vi没有输入
if("".equals(ViInput.getText()) || ViInput.getText() == null){
JOptionPane.showMessageDialog(null, "请输入Vi数据!", "字段空缺", JOptionPane.INFORMATION_MESSAGE);
return false;
}
else
// Vi的长度要求是8个字符,此处初始向量要求64bit
if(ViInput.getText().length() != 8) {
JOptionPane.showMessageDialog(null, "Vi必须是8个字符!", "字段格式错误", JOptionPane.INFORMATION_MESSAGE);
return false;
}
return true;
}
}
(文本文件加密界面)FileEncrptFrame.java
package com.zmn.main;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileEncrptFrame extends JFrame{
private int currentHeight = 10; // 绝对布局高度控制变量,每次添加一行组件之后该变量要增加添加最大组件的高度+10(两行的间隔)
private JTextArea EmessageArea,DmessageArea;
private JTextField key1Input, key2Input, ViInput;
private JComboBox<String> workModeCbx;
private JButton encryptBtn, decipheringBtn;
private Des1 des = new Des1Impl(); // 加密解密用到的对象
public FileEncrptFrame(){
int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width; // 屏幕宽度
int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height; // 屏幕高度
int frmWidth = 400; // 窗口宽度
int frmHeight = 450; // 窗口高度
setBounds((screenWidth - frmWidth)/2, (screenHeight - frmHeight)/2, frmWidth, frmHeight);
setTitle("字符串加密");
setLayout(null);
// 初始化各个组件,初始化界面
initElement();
//添加动态监听事件
addElementListener();
}
// 初始化各个组件,初始化界面
private void initElement(){
JTextField EmessageField,DmessageField, key1Field, key2Field, workModeField, ViField;
EmessageField = new JTextField("密文文件路径:");
DmessageField = new JTextField("明文文件路径:");
key1Field = new JTextField("密钥1(8字符):");
key2Field = new JTextField("密钥2(8字符):");
ViField = new JTextField("初始向量Vi:");
workModeField = new JTextField("工作模式:");
EmessageArea = new JTextArea("input Emessage Here", 5, 30);
DmessageArea = new JTextArea("input Dmessage here", 5,30);
key1Input = new JTextField();
key2Input = new JTextField();
ViInput = new JTextField();
encryptBtn = new JButton("加密");
decipheringBtn = new JButton("解密");
workModeCbx = new JComboBox<>();
workModeCbx.addItem("ECB");
workModeCbx.addItem("CBC");
workModeCbx.addItem("CFB");
workModeCbx.addItem("OFB");
DmessageField.setEditable(false);
DmessageField.setBorder(null);
DmessageField.setBounds(10,currentHeight,90,30);
DmessageArea.setBounds(100,currentHeight,8*30, 30*10);
DmessageArea.setLineWrap(true); // 自动换行
DmessageArea.setWrapStyleWord(true); // 断行不断字
JScrollPane dmessageAreaScroll = new JScrollPane(DmessageArea);
dmessageAreaScroll.setBounds(100,currentHeight,8*30, 30*3);
dmessageAreaScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
currentHeight += dmessageAreaScroll.getHeight()+10;
key1Field.setEditable(false);
key1Field.setBorder(null);
key1Field.setBounds(5,currentHeight,90,30);
key1Input.setBounds(100,currentHeight,240,30);
currentHeight += key1Input.getHeight()+10;
key2Field.setEditable(false);
key2Field.setBorder(null);
key2Field.setBounds(5,currentHeight,90,30);
key2Input.setBounds(100,currentHeight,240,30);
currentHeight += key2Input.getHeight()+10;
ViField.setEditable(false);
ViField.setBorder(null);
ViField.setBounds(5,currentHeight,80,30);
ViField.setEditable(false);
ViInput.setBounds(100,currentHeight,240,30);
ViInput.setEditable(false);
currentHeight += ViInput.getHeight()+10;
EmessageField.setEditable(false);
EmessageField.setBorder(null);
EmessageField.setBounds(10,currentHeight,90,30);
EmessageArea.setBounds(100,currentHeight,8*30, 30*10);
EmessageArea.setLineWrap(true); // 自动换行
EmessageArea.setWrapStyleWord(true); // 断行不断字
JScrollPane emessageAreaScroll = new JScrollPane(EmessageArea);
emessageAreaScroll.setBounds(100,currentHeight,8*30, 30*3);
emessageAreaScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
currentHeight += emessageAreaScroll.getHeight()+10;
workModeField.setEditable(false);
workModeField.setBorder(null);
workModeField.setBounds(20,currentHeight,80,30);
workModeCbx.setBounds(150,currentHeight,100,30);
currentHeight += workModeCbx.getHeight()+10;
encryptBtn.setBounds(100,currentHeight,80,30);
decipheringBtn.setBounds(210,currentHeight,80,30);
currentHeight += decipheringBtn.getHeight()+10;
add(DmessageField);add(dmessageAreaScroll);
add(key1Field);add(key1Input);
add(key2Field);add(key2Input);
add(ViField);add(ViInput);
add(EmessageField);add(emessageAreaScroll);
add(workModeField); add(workModeCbx);
add(encryptBtn);add(decipheringBtn);
}
// 增加组件的事件监听者
private void addElementListener(){
encryptBtn.addActionListener(new FileEncrptFrame.BtnActionListener());
decipheringBtn.addActionListener(new FileEncrptFrame.BtnActionListener());
workModeCbx.addItemListener(new FileEncrptFrame.comBoxChangeListener());
}
// 加密/解密 按钮事件监听者类
class BtnActionListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
String Emessage = EmessageArea.getText();
String Dmessage = DmessageArea.getText();
String key1 = key1Input.getText();
String key2 = key1Input.getText();
// 电码本ECB ECB_MODE = 0;
// 密码分组链接模式(CBC) CBC_MODE = 1;
// 密码反馈模式(CFB) CFB_MODE = 2;
// 输出反馈模式(OFB) OFB_MODE = 3;
int mode = workModeCbx.getSelectedIndex();
String Vi = ViInput.getText();
FileInputStream fin = null;
FileOutputStream fout = null;
byte[] b = null;
if(e.getSource() == encryptBtn){
if(!judgeInputNotNull(0, mode))
return ;
System.out.println("DmessageLocation:" + Dmessage);
try {
fin = new FileInputStream(Dmessage);
fout = new FileOutputStream(Emessage);
while (fin.available() > 0) {
if (fin.available() > 1024)
b = new byte[1024];
else
b = new byte[fin.available()];
fin.read(b);
fout.write(des.TripleDES_Encrypt_GivenMode(new String(b), key1, key2, mode, Vi).getBytes());
}
fin.close();
fout.close();
}catch(IOException e1){
JOptionPane.showMessageDialog(null, "加密失败,仅支持文本类型文件加密!", "加密失败", JOptionPane.ERROR_MESSAGE);
//e1.printStackTrace();
System.out.println(e1.getMessage());
}
JOptionPane.showMessageDialog(null, "加密成功!", "成功", JOptionPane.PLAIN_MESSAGE);
System.out.println("EmessageLocation:" + Emessage);
}else if(e.getSource() == decipheringBtn){
if(!judgeInputNotNull(1, mode))
return ;
System.out.println("EmessageLocation:" + Emessage);
try {
fin = new FileInputStream(Emessage);
fout = new FileOutputStream(Dmessage);
while (fin.available() > 0) {
if (fin.available() > 1024)
b = new byte[1024];
else
b = new byte[fin.available()];
fin.read(b);
fout.write(des.TripleDES_deciphering_GivenMode(new String(b), key1, key2, mode, Vi).getBytes());
}
fin.close();
fout.close();
}catch(IOException e1){
JOptionPane.showMessageDialog(null, "解密失败,仅支持文本类型文件加密!", "解密失败", JOptionPane.ERROR_MESSAGE);
// e1.printStackTrace();
System.out.println(e1.getMessage());
}
JOptionPane.showMessageDialog(null, "解密成功!", "成功", JOptionPane.PLAIN_MESSAGE);
System.out.println("DmessageLocation:" + Dmessage);
}
}
}
// 下拉列表事件监听者类
class comBoxChangeListener implements ItemListener{
@Override
public void itemStateChanged(ItemEvent e) {
if(e.getSource() == workModeCbx && e.getStateChange() == ItemEvent.SELECTED){
if(workModeCbx.getSelectedIndex() > 1){
ViInput.setEditable(true);
}else{
ViInput.setEditable(false);
}
}
}
}
/**
* 判断加密/解密中,对应的信息是否不为空/正确,给出友好的用户提示信息
* @param EorD 加密还是解密,0表示加密,1表示解密
* @param mode 工作模式,由此来判断是否需要Vi作为初始向量 0表示ECB,1表示CBC,2表示CFB,3表示OFB
* @return boolean 验证通过返回true,不通过返回false
*/
private boolean judgeInputNotNull(int EorD, int mode){
if(EorD == 0) // 加密还是解密,0表示加密,1表示解密
//明文数据没有输入
if("".equals(DmessageArea.getText()) || DmessageArea.getText() == null){
JOptionPane.showMessageDialog(null, "请输入明文数据!", "字段空缺",JOptionPane.INFORMATION_MESSAGE);
return false;
}else{
File file = new File(DmessageArea.getText());
if(!file.exists()){
JOptionPane.showMessageDialog(null, "明文文件不存在!", "文件未找到",JOptionPane.INFORMATION_MESSAGE);
return false;
}
}
else
//密文数据没有输入
if("".equals(EmessageArea.getText()) || EmessageArea.getText() == null){
JOptionPane.showMessageDialog(null, "请输入密文数据!", "字段空缺",JOptionPane.INFORMATION_MESSAGE);
return false;
}else{
File file = new File(EmessageArea.getText());
if(!file.exists()){
JOptionPane.showMessageDialog(null, "密文文件不存在!", "文件未找到",JOptionPane.INFORMATION_MESSAGE);
return false;
}
}
//key1没有输入
if("".equals(key1Input.getText()) || key1Input.getText() == null) {
JOptionPane.showMessageDialog(null, "请输入key1数据!", "字段空缺", JOptionPane.INFORMATION_MESSAGE);
return false;
}
else
// key1的长度要求是8个字符,此处子密钥要求64bit
if(key1Input.getText().length() != 8) {
JOptionPane.showMessageDialog(null, "key1必须是8个字符!", "字段格式错误", JOptionPane.INFORMATION_MESSAGE);
return false;
}
//key2没有输入
if("".equals(key2Input.getText()) || key2Input.getText() == null){
JOptionPane.showMessageDialog(null, "请输入key2数据!", "字段空缺", JOptionPane.INFORMATION_MESSAGE);
return false;
}
else
// key1的长度要求是8个字符,此处子密钥要求64bit
if(key2Input.getText().length() != 8) {
JOptionPane.showMessageDialog(null, "key2必须是8个字符!", "字段格式错误", JOptionPane.INFORMATION_MESSAGE);
return false;
}
if(mode >= Des1.CFB_MODE) // CFB和OFB都需要Vi作为初始向量
// Vi没有输入
if("".equals(ViInput.getText()) || ViInput.getText() == null){
JOptionPane.showMessageDialog(null, "请输入Vi数据!", "字段空缺", JOptionPane.INFORMATION_MESSAGE);
return false;
}
else
// Vi的长度要求是8个字符,此处初始向量要求64bit
if(ViInput.getText().length() != 8) {
JOptionPane.showMessageDialog(null, "Vi必须是8个字符!", "字段格式错误", JOptionPane.INFORMATION_MESSAGE);
return false;
}
return true;
}
}
测试工具类(方便调试中间的每一步过程,这里也提供了一个测试工具类,将二进制数据每8bit隔开)TestMetod.java:
package com.zmn.test;
/**
* @author zhuangmingnan
* 测试工具类
*/
public class TestMethod {
/* 每8个比特用空白符分割,易于测试 */
public static void printEvery8BitSplitBySpace(String byteMessage){
char[] c = byteMessage.toCharArray();
for(int i = 0; i < byteMessage.length() / 8; i++){
for(int j = 0 ; j < 8; j ++){
System.out.print(c[ i * 8 + j]);
}
System.out.print(" ");
}
System.out.println();
}
}
最后附上DES实现中用到的各种置换表和压缩、拓展表Table.java:
package com.zmn.table;
public class Table {
//初始置换IP表,下标从0-63共64位
public static int[] initialPermutationIP =
{
58,50,42,34,26,18,10,2,
60,52,44,36,28,20,12,4,
62,54,46,38,30,22,14,6,
64,56,48,40,32,24,16,8,
57,49,41,33,25,17, 9,1,
59,51,43,35,27,19,11,3,
61,53,45,37,29,21,13,5,
63,55,47,39,31,23,15,7
};
//初始逆置换IP表,下标从0-63共64位
public static int[] initialInversePermutationIP = {
40,8,48,16,56,24,64,32,
39,7,47,15,55,23,63,31,
38,6,46,14,54,22,62,30,
37,5,45,13,53,21,61,29,
36,4,44,12,52,20,60,28,
35,3,43,11,51,19,59,27,
34,2,42,10,50,18,58,26,
33,1,41, 9,49,17,57,25
};
//选择扩展E表,下标从0-47共48位
public static int[] selectiveExtensionsE = {
32,1,2,3,4,5,
4,5,6,7,8,9,
8,9,10,11,12,13,
12,13,14,15,16,17,
16,17,18,19,20,21,
20,21,22,23,24,25,
24,25,26,27,28,29,
28,29,30,31,32,1
};
//置换选择PC-1
public static int[] substitutionSelectionPC1 = {
57,49,41,33,25,17,9,
1,58,50,42,34,26,18,
10,2,59,51,43,35,27,
19,11,3,60,52,44,36,
63,55,47,39,31,23,15,
7,62,54,46,38,30,22,
14,6,61,53,45,37,29,
21,13,5,28,20,12,4
};
//置换选择PC-2
public static int[] substitutionSelectionPC2 = {
14,17,11,24,1,5,
3,28,15,6,21,10,
23,19,12,4,26,8,
16,7,27,20,13,2,
41,52,31,37,47,55,
30,40,51,45,33,48,
44,49,39,56,34,53,
46,42,50,36,29,32
};
//S盒
public static int[][] SBox = {
{
14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7
,0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8
,4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0
,15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13
},{
15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10
,3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5
,0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15
,13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9
},{
10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8
,13,7,0,9,3,4,6,10,2,8,5,14,13,11,15,1
,13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7
,1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12
},{
7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15
,13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9
,10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4
,3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14
}, {
2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9
,14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6
,4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14
,11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3
},{
12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11
,10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8
,9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6
,4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13
},{
4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1
,13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6
,1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2
,6,11,12,8,1,4,10,7,9,5,0,15,14,2,3,12
}, {
13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7
,1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2
,7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8
,2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11
}};
// 置换P表
public static int[] substitutionP = {
16,7,20,21,
29,12,28,17,
1,15,23,26,
5,18,31,10,
2,8,24,14,
32,27,3,9,
19,13,30,6,
22,11,4,25
};
}
最后附上简单的界面效果图:
初始界面
加密界面
该实现通过测试能实现基本的加密解密,获取二进制是通过Integer.toBinaryString()方法对字符串逐一生成二进制字符串后连接成64bit数据。
EBC、CBC模式下如果明文长度不是8字符的倍数会在明文后面自动补0以达到需要的分组长度。
该加密解密实现目前只实现字符串的加密解密、文本文件的加密解密,如果对其他类型(特别是二进制类型加密解密会抛出异常,暂未解决)
欢迎大家交流探讨,共同学习!