记录与分享密码学编程实现,为方便自身学习记录;具体的代码说明,程序文件中皆有比较完善的注释,此处不进行过多的阐述;代码均会有可优化,完善之处,可自行对程序进行优化。(迁移 2023-06-03 记录)
1. Caesar
凯撒密码的实现简单,本次主要实现的是图形化界面,建议使用JDK8或11,自身携带图形库。
package cryptograghy;
//import javafx.application.Application;
//import javafx.geometry.Insets;
//import javafx.scene.Scene;
//import javafx.scene.control.Button;
//import javafx.scene.control.Label;
//import javafx.scene.control.TextField;
//import javafx.scene.layout.VBox;
//import javafx.stage.Stage;
//启动图形界面需要 JDK1.8 或 手动导包
/**
* 凯撒密码
* @author wengui
*/
public class Caesar{
//public class Caesar extends Application {
/*
@Override
public void start(Stage primaryStage) {
//创建标题
primaryStage.setTitle("凯撒密码");
//创建输入框,对应下边 lable标签
TextField text = new TextField();
TextField offsetFiled = new TextField();
TextField encipherField = new TextField();
TextField decryptField = new TextField();
//创建三个说明标签
Label textLabel = new Label("请输入明文/密文:");
Label offsetLabel = new Label("请输入偏移量:");
Label encipherLabel = new Label("加密后:");
Label decryptLabel = new Label("原密文:");
//操作按钮
Button button = new Button("加/解密");
button.setOnAction(e -> {
int offset = Integer.parseInt(offsetFiled.getText());
String plainText = caesarText(text.getText(), offset);
String cipherText = caesarText(plainText, -offset);
encipherField.setText(plainText);
decryptField.setText(cipherText);
});
VBox vbox = new VBox(10);
vbox.setPadding(new Insets(10));
vbox.getChildren().addAll(textLabel, text, offsetLabel, offsetFiled, button,encipherLabel, encipherField
,decryptLabel, decryptField);
Scene scene = new Scene(vbox, 300, 280);
primaryStage.setScene(scene);
primaryStage.show();
}
*/
/**
* 凯撒加/解密
* @param orgin 明文
* @param offset 偏移量
* @return 密文
*/
public static String caesarText(String orgin, int offset){
StringBuffer caesarStr = new StringBuffer();
for (int i = 0; i < orgin.length(); i++) {
char c = (char)('a' + (orgin.charAt(i) - 'a' + offset + 26) % 26);
caesarStr.append(c);
}
return caesarStr.toString();
}
/**
* 凯撒解密
* @param ciphertext 密文
* @param offset 偏移量
* @return 明文
*/
public static String decrypt(String ciphertext, int offset){
StringBuffer plaintext = new StringBuffer();
for (int i = 0; i < ciphertext.length(); i++) {
char c = (char)('a' + (ciphertext.charAt(i) - offset - 'a' + 26) % 26);
plaintext.append(c);
}
return plaintext.toString();
}
/*
/**
* 启动入口
* @param args
* /
public static void main(String[] args) {
launch(args);
}
*/
}2. RC4
函数功能:使实现将任意长度的的字符串进行RC4加密。
package cryptograghy;
import java.util.Scanner;
/**
* RC4密码
* @author wengui
*/
public class RC4 {
public final static int SBOX_LENGHT = 256;
/**
* 伪随机 S 盒生成器
* @param key 密钥
* @return S盒 char[]
*/
public static char[] generateSBox(char[] key) {
char[] sBox = new char[256];
int kLen = key.length - 1;
for (int i = 0; i < 256; i++) {
sBox[i] = (char) i;
}
int j = 0;
for (int i = 0; i < 256; i++) {
j = (j + sBox[i] + key[i & kLen]) & 0xFF;
swap(sBox, i, j);
}
return sBox;
}
/**
* 数组元素交换
* @param arr 目标数组
* @param i 参数1
* @param j 参数2
*/
private static void swap(char[] arr, int i, int j) {
char temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
/**
* 密钥流生成器
* @param sBox S盒
* @param mSize 明文长度
* @return 密钥流 char[]
*/
public static char[] generateKeyStream(char[] sBox, int mSize) {
char[] keyStream = new char[mSize];
int i = 0, j = 0;
//使用 位运算 代替 模运算
for (int k = 0; k < mSize; k++) {
i = (i + 1) & 0xff;
j = (j + sBox[i]) & 0xff;
swap(sBox, i, j);
keyStream[k] = sBox[(sBox[i] + sBox[j]) & 0xff];
}
return keyStream;
}
/**
* RC4 加密
* @param plainText 明文
* @param keyStream 密钥流
* @return 密文 chat[]
*/
public static char[] encipher(char[] plainText, char[] keyStream) {
int len = plainText.length;
char[] ciphertext = new char[len];
//同位亦或运算
for (int i = 0; i < len; i++) {
ciphertext[i] = (char) (plainText[i] ^ keyStream[i]);
}
return ciphertext;
}
/**
* RC4 解密
* @param ciphertext 密文
* @param keyStream 密钥流
* @return 明文 char[]
*/
public static char[] decipher(char[] ciphertext, char[] keyStream){
return encipher(ciphertext, keyStream);
}
/**
* 启动
*/
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter plaintext: ");
char[] plaintext = scanner.nextLine().toCharArray();
System.out.println("plaintext(0-1): ");
for (char c : plaintext) {
//此处使用 format 是因为Integer会默认把前导的 0 舍去
System.out.print(String.format("%8s", Integer.toBinaryString(c)).replace(' ', '0') + " ");
}
System.out.println();
System.out.print("Enter key: ");
char[] key = scanner.next().toCharArray();
char[] sBox = generateSBox(key);
char[] keyStream = generateKeyStream(sBox, plaintext.length);
char[] ciphertext = encipher(plaintext, keyStream);
char[] decryptedText = decipher(ciphertext, keyStream);
String keySStr = new String(keyStream);
System.out.println("keyStream --> " + keySStr);
System.out.println("keyStream(0-1): ");
for (char c : keySStr.toCharArray()) {
System.out.print(String.format("%8s", Integer.toBinaryString(c)).replace(' ', '0') + " ");
}
System.out.println("\n");
String cipherStr = new String(ciphertext);
System.out.println("Ciphertext --> " + cipherStr);
System.out.println("Ciphertext(0-1): ");
for (char c : cipherStr.toCharArray()) {
System.out.print(String.format("%8s", Integer.toBinaryString(c)).replace(' ', '0') + " ");
}
System.out.println("\n");
String decrypStr = new String(decryptedText);
System.out.println("Decrypted text --> " + decrypStr);
System.out.println("Decrypted text(0-1): ");
for (char c : decrypStr.toCharArray()) {
System.out.print(String.format("%8s", Integer.toBinaryString(c)).replace(' ', '0') + " ");
}
System.out.println("\n");
}
}✨运行示例:
#输入要求: 任意长度字符
#示例--1
Enter plaintext: wweqw
plaintext(0-1):
01110111 01110111 01100101 01110001 01110111
Enter key: qeqe
keyStream --> LI¥(
keyStream(0-1):
01001100 01001001 10011111 10100101 00101000
Ciphertext --> ;>úÔ_
Ciphertext(0-1):
00111011 00111110 11111010 11010100 01011111
Decrypted text --> wweqw
Decrypted text(0-1):
01110111 01110111 01100101 01110001 01110111 3. DES
✨函数功能:使用字符串操作来代替位运算,实现将64bit的字符串进行DES加密,对于不足的字符串会自动进行补"0"填充。
package cryptograghy;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
/**
* DES 算法
* @author wengui
*/
public class DES {
/**
* 子密钥
*/
private static final String[] KEYS = new String[16];
/**
* 明文长度
*/
private static int len;
/**
* 置换
* 根据 置换表 返回 新 二进制字符串
* @param ori 原二进制字符串
* @param table 置换表
* @return 置换后 新 二进制字符串
*/
private static String permutation(String ori, int[] table){
StringBuilder des = new StringBuilder(table.length);
//value 遍历 table 中的值
for (int value : table) {
des.append(ori.charAt(value - 1));
}
return des.toString();
}
/**
* 子密钥 生成器
* @param key 密钥
*/
private static void generateKeys(String key) {
//PC1 置换 => 56位有效秘钥
String validKey = permutation(key, DES_Rules.PC1_TABLE);
String l = validKey.substring(0, 28);
String r = validKey.substring(28, 56);
//16轮 循环移位
for (int i = 0; i < 16; i++) {
// 移位 : 字符串截断拼接实现
int j = DES_Rules.LS_TABLE[i];
l = l.substring(j) + l.substring(0, j);
r = r.substring(j) + r.substring(0, j);
// 左右 56位 密钥 => PC2 置换 => 48位 子密钥
KEYS[i] = permutation(l + r, DES_Rules.PC2_TABLE);
}
}
/**
* 16轮 迭代加密
* @param plainText 明文
* @param method 加/解密
* @return 字符串
*/
private static String round16(String plainText , String method){
String left = plainText.substring(0, 32);
String right = plainText.substring(32);
String temp;
if("encrypt".equals(method)){
for (int i = 0; i < 16; i++) {
temp = right;
String o = feistel(right, KEYS[i]);
//右半 部分参与运算
right = xor(left, o);
//原右半 部分变为 左半 部分
left = temp;
}
}else if("decrypt".equals(method)){
for (int i = 15; i >= 0; i--) {
temp = right;
//右半 部分参与运算
right = xor(left, feistel(right, KEYS[i]));
//原右半 部分变为 左半 部分
left = temp;
}
}
//循环中最后一次 左右部分 已调换
return right + left;
}
/**
* f 函数
* @param ori 参与运算的部分
* @param key 子密钥
* @return 运算后 二进制字符串
*/
private static String feistel(String ori, String key){
//E 拓展 => 48位
ori = permutation(ori, DES_Rules.E_TABLE);
//异或
ori = xor(ori, key);
//s 盒压缩 => 32位
ori = sBoxCompression(ori);
//p 盒置换
ori = permutation(ori, DES_Rules.P_TABLE);
return ori;
}
/**
* 异或运算
* @param str1 字符串1
* @param str2 字符串2
* @return 运算后字符串
*/
private static String xor(String str1, String str2) {
int len = str1.length();
//安全检验
if(str2.length() != len){
System.out.println("xor(): 参数长度不相同");
System.exit(0);
}
StringBuilder result = new StringBuilder(48);
for (int i = 0; i < len; i++) {
if (str1.charAt(i) == str2.charAt(i)) {
result.append("0");
} else {
result.append("1");
}
}
return result.toString();
}
/**
* S 盒压缩
* @param input 48位字符
* @return 32位字符
*/
private static String sBoxCompression(String input) {
if(input.length() != 48){
System.out.println("sBoxCompression(): 处理非48位字符串");
System.exit(0);
}
StringBuilder des = new StringBuilder(32);
//分组压缩
for (int i = 0; i < 8; i++) {
String block = input.substring(i * 6, (i + 1) * 6);
//将 二进制 字符串转换 10进制
int row = Integer.parseInt(String.valueOf(block.charAt(0)) + block.charAt(5), 2);
int col = Integer.parseInt(block.substring(1, 5), 2);
int value = DES_Rules.S_BOX[i][row][col];
des.append(String.format("%4s", Integer.toBinaryString(value)).replace(' ', '0'));
}
return des.toString();
}
/**
* 加密
*
* @param plaintext 明文
* @param key 密钥
* @return 密文
*/
public static String encrypt(String plaintext, String key){
/*
// 以下设计便于外部 调用
*/
if(plaintext.length() > 8 || key.length() > 8){
System.out.println("明文/秘钥 须在8个字节以内 !");
System.exit(0);
}
//预处理: 转换二进制字符串,位数填充
plaintext = preHandle(plaintext);
key = preHandle(key);
//子密钥生成
generateKeys(key);
plaintext = round16(permutation(plaintext, DES_Rules.IP_TABLE), "encrypt");
return permutation(plaintext, DES_Rules.IPINV_TABLE);
}
/**
* 解密
*
* @param ciphertext 密文
* @param key 密钥
* @return 明文
*/
private static String decrypt(String ciphertext, String key){
/*
// 考以下设计便于外部 调用
*/
if(ciphertext.length() != 64 ){
//这里可填充多个验证函数,本处暂不实现
System.out.println("密文不符合要求(非64位,非0-1字符串) !");
System.exit(0);
}
if( key.length() > 8){
System.out.println("秘钥 须在8个字节以内 !");
System.exit(0);
}
//长远考虑:便于外部可直接传入 密文 和 key 调用,所以重复处理
//预处理: 转换二进制字符串,位数填充
key = preHandle(key);
//子密钥生成
generateKeys(key);
ciphertext = round16(permutation(ciphertext, DES_Rules.IP_TABLE), "decrypt");
return permutation(ciphertext, DES_Rules.IPINV_TABLE);
}
/**
* 二进制字符串 转换 字符字符串
* @param ori 二进制字符串
* @return 字符字符串
*/
private static String binaryToString(String ori) {
int length = ori.length()/8;
StringBuilder result = new StringBuilder(length);
for (int i = 0; i < length; i++) {
String b = ori.substring(i*8, (i+1)*8);
// 将二进制字符串转换为十进制整数
int decimal = Integer.parseInt(b, 2);
// 将整数转换为字符
char character = (char) decimal;
result.append(character);
}
return result.substring(0,len);
}
/**
* 字符预处理
* 转换二进制字符串, 补足位数
* @param ori 原字符串
* @return 64位二进制字符串
*/
private static String preHandle(String ori){
// 64位初始秘钥 二进制字符串
StringBuilder binaryString = new StringBuilder(64);
byte[] bytes = ori.getBytes(StandardCharsets.UTF_8);
for (byte aByte : bytes) {
//转换 二进制
String bitStr = Integer.toBinaryString(aByte);
//确定 补0 位数
int len = 8 - bitStr.length();
if (len > 0) {
bitStr = String.format("%0" + len + "d", 0) + bitStr;
}
binaryString.append(bitStr);
}
// 补齐64位
while (binaryString.length() < 64) {
binaryString.append(String.format("%08d", 0));
}
return binaryString.toString();
}
/**
* 主函数
* @param args 参
*/
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入明文:\t");
String plainText = scanner.next();
//便于测试观察
System.out.println("明文转换(0-1):" + preHandle(plainText));
//记录明文长度,便于解码转换
len = plainText.length();
System.out.print("请输入秘钥:\t");
String key = scanner.next();
String cipher = encrypt(plainText, key);
System.out.println("加密结果(0-1): " + cipher);
System.out.println("加密结果:" + binaryToString(cipher));
String decryptText = decrypt(cipher, key);
System.out.println("解密结果(0-1):"+decryptText);
System.out.println("解密结果:" + binaryToString(decryptText));
}
}package cryptograghy;
public class DES_Rules {
/**
* IP置换表
*/
public static final int[] IP_TABLE = {
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逆置换表
*/
public static final int[] IPINV_TABLE = {
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扩展表
*/
public static final int[] E_TABLE = {
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
};
/**
* P置换表
*/
public static final int[] P_TABLE = {
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
};
/**
* PC1置换表
*/
public static final int[] PC1_TABLE = {
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
};
/**
* PC2置换表
*/
public static final int[] PC2_TABLE = {
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
};
/**
* 左移位数表
*/
public static final int[] LS_TABLE = {
1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1
};
/**
* S盒
*/
public static final int[][][] S_BOX = {
//S1
{
{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}
},
//S2
{
{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}
},
//S3
{
{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, 12, 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}
},
//S4
{
{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},
},
//S5
{
{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}
},
//S6
{ {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}
},
//S7
{
{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, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}
},
//S8
{
{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}
}
};
}运行示例:
#输入要求: 长度不过8的字符串
#示例--1
请输入明文: sdde
明文转换(0-1):0111001101100100011001000110010100000000000000000000000000000000
请输入秘钥: sde
加密结果(0-1): 1110110111111000110101010011100111101111101010011010101110111110
加密结果:íøÕ9
解密结果(0-1):0111001101100100011001000110010100000000000000000000000000000000
解密结果:sdde
#示例--2
请输入明文: qwertyui
明文转换(0-1):0111000101110111011001010111001001110100011110010111010101101001
请输入秘钥: qwertyui
加密结果(0-1): 1111010001011100111111000011100000011011100000110001011101111011
加密结果:ô\ü8{
解密结果(0-1):0111000101110111011001010111001001110100011110010111010101101001
解密结果:qwertyui注意:
实际加密的明文并非输入的明文,而是进行“0”填充加密后的字符串,解密结果也只是截取了原明文长度,正常明文密文的长度是相同的。
程序输入使用的是sc.next(),读到空格会结束输入,若想实现如“Hi RC!”的加密,可改为sc.nextline()。
4. SM4
4.1 SM4
函数功能:使用位运算,实现将128bit的16进制字符串进行SM4加密,对于不足的字符串会自动进行补"0"填充。
package cryptograghy;
import java.util.Scanner;
/**
* SM4
* @author wengui
*/
public class SM4 {
/**
* 子密钥
*/
private static final int [] RK = new int[32];
private static final int [] FK = {0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc};
private static final int [] CK = {
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
};
private static final int [] S_BOX = {
0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05,
0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x04,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x06,0x99,
0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0x0b,0x43,0xed,0xcf,0xac,0x62,
0xe4,0xb3,0x1c,0xa9,0xc9,0x08,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6,
0x47,0x07,0xa7,0xfc,0xf3,0x73,0x17,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8,
0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b,0xf8,0xeb,0x0f,0x4b,0x70,0x56,0x9d,0x35,
0x1e,0x24,0x0e,0x5e,0x63,0x58,0xd1,0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,0x87,
0xd4,0x00,0x46,0x57,0x9f,0xd3,0x27,0x52,0x4c,0x36,0x02,0xe7,0xa0,0xc4,0xc8,0x9e,
0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1,
0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3,
0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0x0d,0x53,0x4e,0x6f,
0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x03,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,
0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8,
0x0a,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0,
0x89,0x69,0x97,0x4a,0x0c,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x09,0xc5,0x6e,0xc6,0x84,
0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48
};
private static int len;
/**
* 主加/解密函数
* @param ori 明/密文
* @param method 指定方法
* @return 加/解密结果
*/
private static byte[] cryptMainFunc(byte[] ori, int method) {
//用于存储运算数据
int[] xTemp = new int[4];
//将输入以32比特分组
for (int i = 0; i < 4; i++) {
xTemp[i] = byte4ToBit32(ori[4 * i], ori[4 * i + 1], ori[4 * i + 2], ori[4 * i + 3]);
}
//盒变换输入和输出
int boxIn, boxOut;
if(method == 1){
for (int i = 0; i < 32; i++) {
//方法2:通过改变顺序改变模式,但每次循环都计算,弃用
//int index = (method == 0) ? i : (31 - i);
//sBox_input= X(i+1) ⊕ X(i+2) ⊕ X(i+3) ⊕ RK(i)
boxIn = xTemp[1] ^ xTemp[2] ^ xTemp[3] ^ RK[i];
boxOut = sBox(boxIn);
//X(i+4) = X(i) ⊕ sBox_output ⊕ Y2 ⊕ Y10 ⊕ Y18 ⊕ Y24
int temp = xTemp[0] ^ boxOut ^ lShift(boxOut, 2) ^ lShift(boxOut, 10) ^ lShift(boxOut, 18) ^ lShift(boxOut, 24);
xTemp[0] = xTemp[1];
xTemp[1] = xTemp[2];
xTemp[2] = xTemp[3];
xTemp[3] = temp;
}
}else {
for (int i = 31; i >= 0; i--) {
boxIn = xTemp[1] ^ xTemp[2] ^ xTemp[3] ^ RK[i];
boxOut = sBox(boxIn);
int temp = xTemp[0] ^ boxOut ^ lShift(boxOut, 2) ^ lShift(boxOut, 10) ^ lShift(boxOut, 18) ^ lShift(boxOut, 24);
xTemp[0] = xTemp[1];
xTemp[1] = xTemp[2];
xTemp[2] = xTemp[3];
xTemp[3] = temp;
}
}
byte[] des = new byte[16];
//将最后 的 4 int 逆序 并转换为 byte
for (int i = 0; i < 4; i++) {
//每 32bit int 转换为 4 个 byte
System.arraycopy(splitInt(xTemp[3 - i]), 0, des, 4 * i, 4);
}
return des;
}
/**
* 转16进制字符串
* @param ori 源
* @return des
*/
private static String bytesToHexString(byte[] ori) {
StringBuilder des = new StringBuilder();
//字符转换两位16进制字符
for (byte b : ori) {
des.append(String.format("%02X", b));
}
return "0X" + des;
}
/**
* 16进制字符串转换byte[]
* @param ori 源
* @return bytes
*/
private static byte[] hexStringToBytes(String ori) {
StringBuilder str = new StringBuilder(ori.substring(2));
int length = ori.length() - 2;
if(length > 32){
System.out.println("传入 明文/秘钥 输入不符要求 !");
System.exit(0);
}else {
//填充 0
str.append("0".repeat(Math.max(0, 32 - length)));
}
byte[] des = new byte[16];
//每两位合并
for (int i = 0; i < des.length; i++) {
des[i] = (byte) Integer.parseInt(str.substring(i * 2, i * 2 + 2), 16);
}
return des;
}
/**
* 子密钥 生成器
* @param key 密钥
*/
private static void generateKeys(byte[] key) {
//初始密钥 与 临时存储的四个运算密钥
int[] keyTemp = new int[4];
//S 盒变换输入和输出
int boxIn, boxOut;
/*
// k = K ⊕ FK
// 用 128bit 原密钥 生成 4个 初始密钥
*/
for (int i = 0; i < 4; i++) {
//将 4个 8位byte类型 转换为 32位int类型
keyTemp[i] = byte4ToBit32(key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]);
//与 FK 异或
keyTemp[i] ^= FK[i];
}
/*
// RK(i) = K(i) + 4 = K(i) ⊕ T′( K(i+1) ⊕ K(i+2) ⊕ K(i+3) ⊕ CK(i) )
// T′(⋅) = L′( τ(⋅) )
// τ(⋅) = sBox(K(i+1) ⊕ K(i+2) ⊕ K(i+3) ⊕ CK(i))
// L′(B) = B ⊕ (B <<< 13) ⊕ (B <<< 23)
*/
//32轮密钥拓展
for (int i = 0; i < 32; i++) {
// sBox_input = K(i+1) ⊕ K(i+2) ⊕ k(i+3) ⊕ CK(i)
boxIn = keyTemp[1] ^ keyTemp[2] ^ keyTemp[3] ^ CK[i];
// S 盒变换
boxOut = sBox(boxIn);
//密钥生成
RK[i] = keyTemp[0] ^ boxOut ^ lShift(boxOut, 13) ^ lShift(boxOut, 23);
// 重新对 临时密钥 赋值
keyTemp[0] = keyTemp[1];
keyTemp[1] = keyTemp[2];
keyTemp[2] = keyTemp[3];
keyTemp[3] = RK[i];
}
}
/**
* S 盒变换
* @param boxInput 访问位置
* @return 变换值
*/
private static int sBox(int boxInput) {
//将传递进来的 32bit 拆分 8bit
byte[] temp = splitInt(boxInput);
byte[] des = new byte[4];
//S 盒变换
for (int i = 0; i < 4; i++) {
des[i] = (byte) S_BOX[temp[i] & 0xFF];
}
//将4个 8bit 合并为一个 32bit int 作为盒变换输出
return byte4ToBit32(des[0], des[1], des[2], des[3]);
}
/**
* int转换:int => 4byte
* @param n 转换值
* @return 4 x 8bit byte
*/
private static byte[] splitInt(int n) {
//拆分4分,分别返回
return new byte[]{(byte) (n >>> 24), (byte) (n >>> 16),
(byte) (n >>> 8), (byte) n};
}
/**
* 循环左移
* @param ori 左移目标
* @param n 左移量
* @return 左移数据
*/
private static int lShift(int ori, int n) {
//前n位 无符号右移 至最低位
//后32-n位 左移至高位
//两者 或运算
return (ori >>> (32 - n)) | (ori << n);
}
/**
* byte转换:4byte => int
* @param b1 byte1
* @param b2 byte2
* @param b3 byte3
* @param b4 byte4
* @return int
*/
private static int byte4ToBit32(byte b1, byte b2, byte b3, byte b4){
//逐位 按高低 排列
return ((b1 & 0xFF) << 24) | ((b2 & 0xFF) << 16) | ((b3 & 0xFF) << 8) | (b4 & 0xFF);
}
/**
* 加密
* @param plaintext 明文
* @param key 密钥
* @return 密文
*/
public static byte[] encrypt(byte[] plaintext, byte[] key) {
generateKeys(key);
return cryptMainFunc(plaintext, 1);
}
/**
* 解密
* @param ciphertext 密文
* @param key 密钥
* @return 明文
*/
public static byte[] decrypt(byte[] ciphertext, byte[] key) {
//解密也调用密钥生成,为方便外部直接调用
generateKeys(key);
return cryptMainFunc(ciphertext, 0);
}
/**
* 供外部测试
*/
public static void test() {
Scanner sc = new Scanner(System.in);
System.out.println("输入要求: 长度不过32(不算0x)的以0x开头的16进制字符串\n");
System.out.print("请输入明文: ");
String p = sc.next();
len = p.length();
byte[] plaintext = hexStringToBytes(p);
System.out.print("请输入密钥: ");
byte[] key = hexStringToBytes(sc.next());
//换行
System.out.println();
//加密
byte[] cipher = encrypt(plaintext, key);
System.out.println("加密结果: " + bytesToHexString(cipher));
//解密
byte[] decryptText = decrypt(cipher, key);
System.out.println("解密结果: " + bytesToHexString(decryptText).substring(0, len));
}
public static void main(String[] args) {
test();
}
}✨运行示例:
#示例--1
输入要求: 长度不过32(不算0x)的以0x开头的16进制字符串
请输入明文: 0x0123456789abcdeffedcba9876543210
请输入密钥: 0x0123456789abcdeffedcba9876543210
加密结果: 0X681EDF34D206965E86B3E94F536E4246
解密结果: 0X0123456789ABCDEFFEDCBA9876543210
#示例--2
输入要求: 长度不过32(不算0x)的以0x开头的16进制字符串
请输入明文: 0x12345abcdef
请输入密钥: 0x12345fedcba
加密结果: 0X744FDAC2A51690221754356970FEC07B
解密结果: 0X12345ABCDEF注意: 实际加密的明文并非输入的明文,而是进行“0”填充加密后的字符串,解密结果也只是截取了原明文长度,正常明文密文的长度是相同的。
4.2 SM4_2
函数功能: 使用位运算,实现将输入的字符串进行SM4加密,对于不足的字符串会自动进行补"0"填充。
与上诉SM4_1的区别:
可输入任意不以0x,0X开头的长度不超过16的字符串来进行加密
可输入以0x开头的长度不超过32(不包括0x)的16进制字符串来进行加密
package cryptograghy;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Scanner;
/**
* SM4
* @author wengui
* 普通字符的 结果暂未进行合理处理展示,可自行修改
*/
public class SM4_2 {
private static final int [] RK = new int[32];
private static final int [] FK = {0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc};
private static final int [] CK = {
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
};
private static final int [] S_BOX = {
0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05,
0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x04,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x06,0x99,
0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0x0b,0x43,0xed,0xcf,0xac,0x62,
0xe4,0xb3,0x1c,0xa9,0xc9,0x08,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6,
0x47,0x07,0xa7,0xfc,0xf3,0x73,0x17,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8,
0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b,0xf8,0xeb,0x0f,0x4b,0x70,0x56,0x9d,0x35,
0x1e,0x24,0x0e,0x5e,0x63,0x58,0xd1,0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,0x87,
0xd4,0x00,0x46,0x57,0x9f,0xd3,0x27,0x52,0x4c,0x36,0x02,0xe7,0xa0,0xc4,0xc8,0x9e,
0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1,
0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3,
0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0x0d,0x53,0x4e,0x6f,
0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x03,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,
0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8,
0x0a,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0,
0x89,0x69,0x97,0x4a,0x0c,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x09,0xc5,0x6e,0xc6,0x84,
0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48
};
private static int isHexString;
private static int plaintextLength;
/**
* 主加/解密函数
* @param ori 明/密文
* @param method 指定方法
* @return 加/解密结果
*/
private static byte[] cryptMainFunc(byte[] ori, int method) {
//用于存储运算数据
int[] xTemp = new int[4];
//将输入以32比特分组
for (int i = 0; i < 4; i++) {
xTemp[i] = byte4ToInt(ori[4 * i], ori[4 * i + 1], ori[4 * i + 2], ori[4 * i + 3]);
}
//盒变换输入和输出
int boxIn, boxOut;
if(method == 1){
for (int i = 0; i < 32; i++) {
//方法2:通过改变顺序改变模式,但每次循环都计算,弃用
//int index = (method == 0) ? i : (31 - i);
//sBox_input= X(i+1) ⊕ X(i+2) ⊕ X(i+3) ⊕ RK(i)
boxIn = xTemp[1] ^ xTemp[2] ^ xTemp[3] ^ RK[i];
boxOut = sBox(boxIn);
//X(i+4) = X(i) ⊕ sBox_output ⊕ Y2 ⊕ Y10 ⊕ Y18 ⊕ Y24
int temp = xTemp[0] ^ boxOut ^ lShift(boxOut, 2) ^ lShift(boxOut, 10) ^ lShift(boxOut, 18) ^ lShift(boxOut, 24);
xTemp[0] = xTemp[1];
xTemp[1] = xTemp[2];
xTemp[2] = xTemp[3];
xTemp[3] = temp;
}
}else {
for (int i = 31; i >= 0; i--) {
boxIn = xTemp[1] ^ xTemp[2] ^ xTemp[3] ^ RK[i];
boxOut = sBox(boxIn);
int temp = xTemp[0] ^ boxOut ^ lShift(boxOut, 2) ^ lShift(boxOut, 10) ^ lShift(boxOut, 18) ^ lShift(boxOut, 24);
xTemp[0] = xTemp[1];
xTemp[1] = xTemp[2];
xTemp[2] = xTemp[3];
xTemp[3] = temp;
}
}
byte[] des = new byte[16];
//将最后 的 4 int 逆序 并转换为 byte
for (int i = 0; i < 4; i++) {
//每 32bit int 转换为 4 个 byte
System.arraycopy(splitIntToByte(xTemp[3 - i]), 0, des, 4 * i, 4);
}
return des;
}
/**
* 输入预处理:校验,填充,转换0-1字符串
* @param ori 处理目标字符串
* @return 128位字符串
*/
private static byte[] preHandle(String ori, int tag){
//若为16进制字符串
if(ori.startsWith("0x") || ori.startsWith("0X")){
StringBuilder handStr = new StringBuilder(ori.substring(2));
int length = handStr.length();
if(length > 32){
System.out.println("传入 明文/秘钥 输入不符要求 !");
System.out.println("要求:\n\t1.长度不过16的普通字符串\n\t2.长度不过32的以0x开头的16进制字符串");
System.exit(0);
}
//填充 0
handStr.append("0".repeat(Math.max(0, 32 - length)));
//因16进制字符串处理方式 ==> 两位合并至一个字符,若长度为奇数则变为偶数
length = length % 2 == 0 ? length/2 : length/2 + 1;
if(tag == 1){
isHexString = 1;
}
return hexStringToBytes(handStr.toString(), length);
}
if(tag == 1){
plaintextLength = ori.length();
}
//若为 16位 正常字符串
if(ori.length() > 16){
System.out.println("传入 明文/秘钥 输入不符要求 !");
System.out.println("要求:\n\t1.长度不过16的普通字符串\n\t2.长度不过32的以0x开头的16进制字符串");
System.exit(0);
}
//Arrays.copyOf 拷贝 ori 并新建大小为 16,其余位为 0
return Arrays.copyOf(ori.getBytes(StandardCharsets.UTF_8), 16);
}
/**
* 结果处理
* @param ori 源
* @return des
*/
private static String resHandle(byte[] ori){
if(isHexString == 1){
return bytesToHexString(ori);
}
return new String(ori,0,plaintextLength);
}
/**
* 转换: 16进制字符串转换
* @param ori byte数组
* @return 字符字符串
*/
private static byte[] hexStringToBytes(String ori, int length) {
byte[] des = new byte[16];
for (int i = 0; i < length; i++) {
des[i] = (byte) Integer.parseInt(ori.substring(i * 2, i * 2 + 2), 16);
}
return des;
}
/**
* 字符还原
* @param ori 源
* @return des
*/
private static String bytesToHexString(byte[] ori) {
StringBuilder des = new StringBuilder();
//转换16进制字符
for (int i = 0; i < 16; i++) {
des.append(String.format("%02X", ori[i]));
}
return "0X" + des;
}
/**
* 子密钥 生成器
* @param key 密钥
*/
private static void generateKeys(byte[] key) {
//初始密钥 与 临时存储的四个运算密钥
int[] keyTemp = new int[4];
//S 盒变换输入和输出
int sBoxIn, sBoxOut;
/*
// k = K ⊕ FK
// 用 128bit 原密钥 生成 4个 初始密钥
*/
for (int i = 0; i < 4; i++) {
//将 4个 8位byte类型 转换为 32位int类型
keyTemp[i] = byte4ToInt(key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]);
//与 FK 异或
keyTemp[i] ^= FK[i];
}
/*
// RK(i) = K(i) + 4 = K(i) ⊕ T′( K(i+1) ⊕ K(i+2) ⊕ K(i+3) ⊕ CK(i) )
// T′(⋅) = L′( τ(⋅) )
// τ(⋅) = sBox(K(i+1) ⊕ K(i+2) ⊕ K(i+3) ⊕ CK(i))
// L′(B) = B ⊕ (B <<< 13) ⊕ (B <<< 23)
*/
//32轮密钥拓展
for (int i = 0; i < 32; i++) {
// sBox_input = K(i+1) ⊕ K(i+2) ⊕ k(i+3) ⊕ CK(i)
sBoxIn = keyTemp[1] ^ keyTemp[2] ^ keyTemp[3] ^ CK[i];
// S 盒变换
sBoxOut = sBox(sBoxIn);
//密钥生成
RK[i] = keyTemp[0] ^ sBoxOut ^ lShift(sBoxOut, 13) ^ lShift(sBoxOut, 23);
// 重新对 临时密钥 赋值
keyTemp[0] = keyTemp[1];
keyTemp[1] = keyTemp[2];
keyTemp[2] = keyTemp[3];
keyTemp[3] = RK[i];
}
}
/**
* S 盒变换
* @param boxInput 访问位置
* @return 变换值
*/
private static int sBox(int boxInput) {
//将传递进来的 32bit 拆分 8bit
byte[] temp = splitIntToByte(boxInput);
byte[] des = new byte[4];
//S 盒变换
for (int i = 0; i < 4; i++) {
des[i] = (byte) S_BOX[temp[i] & 0xFF];
}
//将4个 8bit 合并为一个 32bit int 作为盒变换输出
return byte4ToInt(des[0], des[1], des[2], des[3]);
}
/**
* int转换:int => 4byte
* @param n 转换值
* @return 4 x 8bit byte
*/
private static byte[] splitIntToByte(int n) {
//拆分4分,分别返回
return new byte[]{(byte) (n >>> 24), (byte) (n >>> 16),
(byte) (n >>> 8), (byte) n};
}
/**
* 循环左移
* @param ori 左移目标
* @param n 左移量
* @return 左移数据
*/
private static int lShift(int ori, int n) {
//前n位 无符号右移 至最低位
//后32-n位 左移至高位
//两者 或运算
return (ori >>> (32 - n)) | (ori << n);
}
/**
* byte转换:4byte => int
* @param b1 byte1 高位
* @param b2 byte2
* @param b3 byte3
* @param b4 byte4 低位
* @return int
*/
private static int byte4ToInt(byte b1, byte b2, byte b3, byte b4){
//逐位 按高低 排列
return ((b1 & 0xFF) << 24) | ((b2 & 0xFF) << 16) | ((b3 & 0xFF) << 8) | (b4 & 0xFF);
}
/**
* 加密
* @param plaintext 明文
* @param key 密钥
* @return 密文
*/
public static byte[] encrypt(byte[] plaintext, byte[] key) {
generateKeys(key);
return cryptMainFunc(plaintext, 1);
}
/**
* 解密
* @param ciphertext 密文
* @param key 密钥
* @return 明文
*/
public static byte[] decrypt(byte[] ciphertext, byte[] key) {
//解密也调用密钥生成,为方便外部直接调用
generateKeys(key);
return cryptMainFunc(ciphertext, 0);
}
/**
* 供外部测试
*/
public static void test() {
Scanner sc = new Scanner(System.in);
System.out.println("输入要求:\n\t1.长度不过16的普通字符串\n\t2.长度不过32的以0x开头的16进制字符串\n");
System.out.print("请输入明文(16以内): ");
String p = sc.next();
byte[] plaintext = preHandle(p, 1);
System.out.print("请输入密钥(16以内): ");
byte[] key = preHandle(sc.next(), 0);
//换行
System.out.println();
//加密
byte[] cipher = encrypt(plaintext, key);
System.out.println("加密结果为: " + resHandle(cipher));
//解密
byte[] decryptText = decrypt(cipher, key);
System.out.println("解密结果为: " + resHandle(decryptText));
}
public static void main(String[] args) {
test();
}
}✨运行示例:
#示例--1
输入要求:
1.长度不过16的普通字符串
2.长度不过32的以0x开头的16进制字符串
请输入明文(16以内): aiudhwudhuca
请输入密钥(16以内): agdywgdygd
加密结果为: ��0��6%Zmy�
解密结果为: aiudhwudhuca
#示例--2
输入要求:
1.长度不过16的普通字符串
2.长度不过32的以0x开头的16进制字符串
请输入明文(16以内): 0x1234567890abcdef12345
请输入密钥(16以内): 0x1234567890abcdef12345
加密结果为: 0X903243CFEED09949E4B49F67CBFEB967
解密结果为: 0X1234567890ABCDEF1234500000000000注意:
实际加密的明文并非输入的明文,而是进行“0”填充加密后的字符串,解密结果也只是截取了原明文长度,正常明文密文的长度是相同的。
程序暂未对结果进行相关合理处理,在输入未达16位的字符串时,加密结果会截取与明文相同长度的字符,并未完整展示全部密文;在输入未达32位的16进制字符串时,解密结果也并未进行截取。
5. RSA
函数功能:实现将任意长度的字符串进行RSA加密。RSA的实现较为简单,比较难处理的是大数的表示以及运算,本程序通过调用java内部类BigInteger来实现。
package cryptograghy;
import java.math.BigInteger;
import java.util.Random;
import java.util.Scanner;
/**
* RSA密码
* @author wengui
*/
public class RSA {
public static final Random random = new Random();
/**
* 密钥长度,一般为1024位或2048位
*/
public static final int BIT_LENGTH = 1024;
/**
* 素性检测的精度
*/
public static final int CERTAINTY = 15;
/**
* 素数检测
* @param n 数
* @param checkCount 精度
* @return ture/false
*/
public static boolean isPrime(BigInteger n, int checkCount) {
// 排除小于等于 1 的数
if (n.compareTo(BigInteger.ONE) <= 0) {
return false;
}
//若为 2 --> ture
if(n.compareTo(BigInteger.valueOf(2)) == 0 ){
return true;
}
//若被 2 整除或为 1 --> false
if (n.mod(BigInteger.TWO).equals(BigInteger.ZERO) || n.compareTo(BigInteger.ONE) == 0) {
return false;
}
//将n - 1写成 2^s * d 形式
BigInteger d = n.subtract(BigInteger.ONE);
int s = 0;
while (d.mod(BigInteger.TWO).equals(BigInteger.ZERO)) {
//s记录偶数因子的个数,
s++;
//d为 n - 1 除以 2^s 后得到的奇数。
d = d.divide(BigInteger.TWO);
}
/*
上面步骤的目的是为了方便进行下一步的计算。根据算法步骤,需要随机选取一个整数a,计算 a^d mod n,其中 d为一个奇数。
将n - 1写成 2^s * d 形式后,就可以将 a^d mod n 的计算拆分成多个步骤,从而避免直接对大整数进行幂运算,提高计算效率。
首先计算a^(d/2) mod n,然后再将结果平方取模n,得到a^d mod n的值,计算复杂度为O(log n),
比直接计算 a^d mod n的复杂度O(n)要小得多。
*/
//Miller-Rabin素性检测
for (int i = 0; i < checkCount; i++) {
//随机选取一个 a, add(BigInteger.ONE) +1, 确保a是一个正整数
BigInteger a = new BigInteger(n.bitLength() - 1, random).add(BigInteger.ONE);
//x = a^d mod n
BigInteger x = a.modPow(d, n);
//若 x == 1或 n - 1
if (x.equals(BigInteger.ONE) || x.equals(n.subtract(BigInteger.ONE))) {
//n可能是素数,继续检测下一个a
continue;
}
//素性标记
boolean isPrime = false;
//计算x^2 mod n、x^4 mod n、x^8 mod n ...
for (int j = 0; j < s - 1; j++) {
//x = x^2 mod n
x = x.modPow(BigInteger.TWO, n);
//若 x = n - 1
if (x.equals(n.subtract(BigInteger.ONE))) {
//不是素数
isPrime = true;
break;
}
}
if (!isPrime) {
return false;
}
}
return true;
}
/**
* 求最大公因数
* @param a 参1
* @param b 参2
* @return 最小公倍数
*/
private static BigInteger gcd(BigInteger a, BigInteger b) {
if (b.equals(BigInteger.ZERO)) {
return a;
} else {
return gcd(b, a.mod(b));
}
}
/**
* 生成e,满足1 < e < φ(n) 且 e 与 φ(n)互质
* @param phi 欧拉函数值
* @param bitLength e的位长
* @return 生成的e值
*/
private static BigInteger generateE(BigInteger phi, int bitLength) {
BigInteger e;
do {
e = new BigInteger(bitLength, random);
//若 e 小于0, 大于 φ(n), 与 φ(n) gcd 不等于1 --> 重新生成
} while (e.compareTo(BigInteger.ONE) <= 0 || e.compareTo(phi) >= 0 || !gcd(e, phi).equals(BigInteger.ONE));
return e;
}
/**
* 求 d
* @param e 私钥
* @param phi φ(n)
* @return 公钥
*/
private static BigInteger generateD(BigInteger e, BigInteger phi) {
return e.modInverse(phi);
}
/**
* 大素数生成
* @param bitLength 比特长度
* @return 大素数
*/
private static BigInteger generatePrimes(int bitLength){
/*
//方式1: 自定义随机生成
BigInteger p;
do {
p = new BigInteger(bitLength, random);
} while (!isPrime(p, CERTAINTY));
return p;
*/
//方式2: 借用 BigInteger 的素数生成, 与方式1 差不多
return BigInteger.probablePrime(bitLength, random);
}
/**
* 加密
* @param message 明文
* @param n 公钥的n
* @param e 公钥的e
* @return 密文
*/
public static byte[] encrypt(byte[] message, BigInteger n, BigInteger e) {
//转换为大整数, 1表示为正数
BigInteger m = new BigInteger(1, message);
// c = m ^ e mod n
BigInteger c = m.modPow(e, n);
return c.toByteArray();
}
/**
* 解密
* @param ciphertext 密文
* @param n 私钥的n
* @param d 私钥的d
* @return 明文
*/
public static byte[] decrypt(byte[] ciphertext, BigInteger n, BigInteger d) {
BigInteger c = new BigInteger(1, ciphertext);
// m = c ^ d mod n
BigInteger m = c.modPow(d, n);
return m.toByteArray();
}
/**
* 外部测试
*/
public static void test() {
Scanner sc = new Scanner(System.in);
BigInteger p = generatePrimes(BIT_LENGTH);
BigInteger q = generatePrimes(BIT_LENGTH);
System.out.println("p = " + p);
System.out.println("q = " + q);
// 计算n
BigInteger n = p.multiply(q);
// 计算 φ(n)
BigInteger phi = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
System.out.println("φ(n) = " + phi);
// 生成e
BigInteger e = generateE(phi, BIT_LENGTH);
// 计算d
BigInteger d = generateD(e, phi);
System.out.println("\n公钥(n, e):");
System.out.println("\tn = " + n);
System.out.println("\te = " + e);
System.out.println("私钥(n, d):");
System.out.println("\tn = " + n);
System.out.println("\td = " + d);
System.out.print("\n请输入明文: ");
String message = sc.next();
// 加密
byte[] ciphertext = encrypt(message.getBytes(), n, e);
System.out.println("\n密文: " + new String(ciphertext));
// 解密
byte[] plaintext = decrypt(ciphertext, n, d);
System.out.println("\n明文: " + new String(plaintext));
}
public static void main(String[] args) {
test();
}
}✨运行示例:
#输入要求: 任意长度的字符串
#示例--1
p = 871951705899788794107135622141
q = 792990757286488926312999762487
φ(n) = 691449643578719389999153648198949390776328227807609843040040
公钥(n, e):
n = 691449643578719389999153648200614333239514505528029978424667
e = 149767581948758359732066190381
私钥(n, d):
n = 691449643578719389999153648200614333239514505528029978424667
d = 548598971881589574659761730898960091399341986367770592960941
请输入明文: Hi, this is RSA!
密文: 237439262252967908532534882306214202673336773169393898006284
密文: %Ӆ���n{��w�{�h�^�
明文: Hi, this is RSA!