實驗二 電子傳輸系統安全-進展2

DKY——仰望星空發表於2024-05-25
任務詳情
  • 上週任務完成情況(程式碼連結,所寫文件等)
  • 本週計劃

上週任務完成情況

  • 採用sm4對儲存的公文進行加密處理,金鑰隨機生成,亂序儲存在資料庫中。其中sm4採用cbc模式,iv固定,跟隨密文一起儲存。解密的時候讀取密文並分離密文和iv,然後解密。
  • SM3加鹽儲存

程式碼

package cn.edu.nuc.article.util;

import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.util.encoders.Hex;

import java.security.Security;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class SM3SaltHelper {
    public static void main(String[] args) {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

        // 原始資料
        byte[] data = "Hello, World!".getBytes();

        // 生成隨機的鹽值
        byte[] salt = generateSalt();

        // 將原始資料與鹽值拼接
        byte[] dataWithSalt = concatBytes(data, salt);

        // 計算SM3雜湊值
        byte[] hash = calculateHash(dataWithSalt);

        // 將鹽值和雜湊值轉換為十六進位制字串
        String saltHex = bytesToHex(salt);
        String hashHex = bytesToHex(hash);

        System.out.println("Salt: " + saltHex);
        System.out.println("Hash: " + hashHex);
    }

    public static String encrypt(String paramStr,byte[]  salt){
        Map<String,String> resultMap=new HashMap<>();
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        // 原始資料
        byte[] data = paramStr.getBytes();

        // 將原始資料與鹽值拼接
        byte[] dataWithSalt = concatBytes(data, salt);

        // 計算SM3雜湊值
        byte[] hash = calculateHash(dataWithSalt);

        // 將鹽值和雜湊值轉換為十六進位制字串
        String hashHex = bytesToHex(hash);
        return  hashHex;
    }


    public static String entryptSM3Password(String plainPassword) {
        byte[] bytesSalt = generateSalt();
        String sm3Password= encrypt(plainPassword,bytesSalt);
        return bytesToHex(bytesSalt)+sm3Password;
    }

    public static byte[] generateSalt() {
        byte[] salt = new byte[8];
        new Random().nextBytes(salt);
        return salt;
    }

    private static byte[] concatBytes(byte[] a, byte[] b) {
        byte[] result = Arrays.copyOf(a, a.length + b.length);
        System.arraycopy(b, 0, result, a.length, b.length);
        return result;
    }

    private static byte[] calculateHash(byte[] input) {
        SM3Digest digest = new SM3Digest();
        digest.update(input, 0, input.length);
        byte[] result = new byte[digest.getDigestSize()];
        digest.doFinal(result, 0);
        return result;
    }

    public static String bytesToHex(byte[] bytes) {
        return Hex.toHexString(bytes);
    }

    public static byte[]  HexTobytes(String hexStr) {

        return Hex.decode(hexStr);
    }




}
package cn.edu.nuc.article.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.Arrays;
import java.util.Random;

public class SM4Tools {
    private static final String name="SM4";                               //演算法名字
    private static final String transformation="SM4/CBC/PKCS5Padding";    //加密模式以及短快填充方式
    private static final String Default_iv="0123456789abcdef";            //加密使用的初始向量

    /**
     * 載入指定檔案,對其進行加密,並將加密結果寫入指定輸出檔案中
     * @param inputFile 要加密的輸入檔案路徑
     * @param outputFile 加密後的輸出檔案路徑
     * @param key 加密所需的金鑰
     * @throws Exception 如果檔案讀取、加密或寫入時出現錯誤,則丟擲異常
     */
    public static void encodeFile(String inputFile, String outputFile, String key) throws Exception {
        // 讀取輸入檔案中的所有位元組
        byte [] inputBytes = Files.readAllBytes(Paths.get(inputFile));
        // 對輸入位元組陣列進行加密
        byte [] encodeByte = encode(inputBytes, key.getBytes(StandardCharsets.UTF_8));
        // 將加密後的位元組陣列寫入指定輸出檔案中
        Files.write(Paths.get(outputFile),encodeByte);
        System.out.println("File encoded successfully.");
    }
    /**
     * 使用指定的加密演算法和金鑰對給定的位元組陣列進行加密
     * @param inputByte 要加密的位元組陣列
     * @param key 加密所需的金鑰
     * @return 加密後的位元組陣列
     * @throws Exception 如果加密時發生錯誤,則丟擲異常
     */
    public static byte [] encode(byte [] inputByte, byte [] key) throws Exception {
        // 獲取加密例項
        Cipher c = Cipher.getInstance(transformation);
        // 根據金鑰的位元組陣列建立 SecretKeySpec
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, name);
        // 建立 IvParameterSpec 物件,使用預設向量和字符集
        IvParameterSpec ivParameterSpec = new IvParameterSpec(Default_iv.getBytes(StandardCharsets.UTF_8));
        // 初始化加密例項
        c.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        // 返回加密後的位元組陣列
        return c.doFinal(inputByte);
    }

    public static void decodeFile(String inputFilePath, String outputFilePath, String key) throws Exception {
        byte[] inputBytes = Files.readAllBytes(Paths.get(inputFilePath));
        byte[] decodeBytes = decode(inputBytes, key.getBytes(StandardCharsets.UTF_8));
        Files.write(Paths.get(outputFilePath), decodeBytes);
        System.out.println("File decode successfully.");
    }



    public static byte[] decode(byte[] inputBytes, byte[] key) throws Exception {
        Cipher cipher = Cipher.getInstance(transformation);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, name);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(Default_iv.getBytes(StandardCharsets.UTF_8));
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipher.doFinal(inputBytes);
    }

    public static String generateRandomString(int length) {
        Random random = new Random();
        StringBuffer string = new StringBuffer();

        for (int i = 0; i < length; i++) {
            // 生成隨機字元(可以根據需要調整範圍)
            int randomChar = random.nextInt(91);
            if (randomChar >= 48 && randomChar <= 57 || // 數字0-9
                    randomChar >= 65 && randomChar <= 90 || // 大寫字母A-Z
                    randomChar >= 97 && randomChar <= 122) { // 小寫字母a-z
                string.append((char) randomChar);
            } else {
                i--; // 重新生成當前位置的字元
            }
        }

        return string.toString();
    }

    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        String inputFile="D:\\data\\test01.docx";              //需要加密的檔案
        String enFile="D:\\data\\test01Encode.docx";               //加密後的檔案
        String deFile="D:\\data\\test01Decode.docx";               //解密後的檔案
        //String key="0123456789ABCDEF";            //加密金鑰,注意必須是128bits,即16個位元組
        String key= generateRandomString(16) ;
        System.out.println(key);
        encodeFile(inputFile,enFile,key);
        decodeFile(enFile,deFile,key);


    }

}

實現效果:
image
image

本週計劃

  • 最後調整最佳化程式碼
  • 邊界測試
  • 增加使用者資料量測試
  • 驗收

相關文章