记录与分享密码学编程实现,为方便自身学习记录;具体的代码说明,程序文件中皆有比较完善的注释,此处不进行过多的阐述;代码均会有可优化,完善之处,可自行对程序进行优化。(迁移 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

注意:

  1. 实际加密的明文并非输入的明文,而是进行“0”填充加密后的字符串,解密结果也只是截取了原明文长度,正常明文密文的长度是相同的。

  2. 程序输入使用的是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

注意:

  1. 实际加密的明文并非输入的明文,而是进行“0”填充加密后的字符串,解密结果也只是截取了原明文长度,正常明文密文的长度是相同的。

  2. 程序暂未对结果进行相关合理处理,在输入未达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!