Java基礎筆記(加密與安全)

1 加密與安全
數據安全:防竊聽,防篡改,防僞造。

摘要算法:確保信息沒有被篡改
對稱加密算法/非對稱加密算法:對數據進行加密/解密
簽名算法:確保信息的完整性和抗否認性

1.1 編碼算法
什麼是編碼,ASCII碼,Unicode,UTF-8這些就是編碼,如字母 A 使用 ASCII 編碼就是 0x41,中文字的 中 使用 Unicode 編碼就是 0x4e2d,使用 UTF-8 編碼就是 0xe4b8ad。

1.1.1 URL 編碼
URL 編碼是瀏覽器發送數據給服務器時所用到的編碼,它是編碼算法而不是加密算法,它的目的是把任意文本數據編碼爲 % 爲前綴表示的文本,編碼後的文本僅包含A-Z,a-z,0-9 以及 -_.*,這是爲了便於瀏覽器和服務器的處理,它的編碼規則是:

A-Z,a-z,0-9 以及 -_.* 保持不變
其它字符以 %XXX 的形式表示,如:小於號 < 編碼爲 %3C,中文字的 中 編碼爲 %E4%B8$AD(0xe4b8ad)
Java 使用 URL編碼需要使用到 URLEncoder ,解碼需要使用 URLDecoder,eg:

String str = "value=中 文";
String url = URLEncoder.encode(str, "UTF-8");
System.out.println(url);
String str2 = URLDecoder.decode(url, "utf-8");
System.out.println(str2);


java的 URLEncoder 與URL編碼的規則有些區別,如空格符, URLEncoder 編碼爲 +,而URL編碼的是 %20,所幸一般的服務器都可以處理這兩種情況

1.1.2 Base64 編碼
Base64編碼是一種編碼算法而不是加密算法,它的目的是把 任意二進制數據編碼爲文本,適用於文本協議。比方說使用筆記本打開一些二進制的文件如 jpg,exe 等文件的時候,會看到一堆亂碼,如果這時候要讓記事本這樣的文本處理軟件能處理二進制數據,就需要一個二進制到字符串的轉換方法,而 Base64 是一種最常見的二進制編碼方法。

1.1.2.1 Base64 編碼的原理
Base64 首先需要一個64個字符的轉化表:

然後如下圖所示,對錶示中文字 中 的UTF-8字節進行base64編碼:

首先對二進制的字符串進行處理,先將它們以每3個字節一組(即 3x8=24 bit)進行劃分
再把劃分好的每一組(24個 bit)重新劃分爲4等份,此時每份正好6個 bit ,得到的 6個 bit 之後再在其頭部填兩個空 bit,從而獲得一個新的字節
此時該字節二進制最大的數值是 00111111,轉化爲十進制最大的數爲 63,最後再依據數值查詢上面的表,就可以獲得四個字符了(圖中的數值是十六進制的)。
如果要編碼的二進制數據不是3的倍數,最後會剩下1個或2個字節怎麼辦?Base64用一個或兩個 0x00 字節在末尾補足,而且還會在編碼的末尾加上1個或2個 = 號,表示補了多少字節,解碼的時候,會自動去掉。

所以,Base64編碼會把3字節的二進制數據編碼爲4字節的文本數據,長度增加 1/3,好處是編碼後的文本數據可以在郵件正文、網頁等直接顯示。

1.1.2.2 base64.getEncoder,base64.getDecoder,base64.getUrlEncoder 和 base64.getUrlDecoder
Java 使用 base64 類的 getEncoder 方法對二進制字節進行編碼,getDecoder 方法對已經編碼的字符串進行解碼,其中由於某些標準的base64編碼在 Url 中會引起衝突,此時需要使用 getUrlEncoder 和 getUrlDecoder,如就是將衝突的 + 和 / 換成 - 號和 _。
eg:

// base64編碼
// 字符串轉成 字節數組後,使用 getEncoder 進行編碼
String bs64 = Base64.getEncoder().encodeToString(string.getBytes("utf-8"));
System.out.println(bs64);

// 使用 getDecoder 進行解碼
String oriString = new String(Base64.getDecoder().decode(bs64), "utf-8");
System.out.println(oriString);

// 某些標準的base64編碼在url中會引起衝突,所以使用getUrlEncoder,其實就是將 + 號和 / 號換成 - 號和 _

bs64 = Base64.getUrlEncoder().encodeToString(string.getBytes("utf-8"));
System.out.println(bs64);
oriString = new String(Base64.getUrlDecoder().decode(bs64), "utf-8");
System.out.println(oriString);


1.2 摘要算法
摘要算法又叫哈希算法(Hash)或數字指紋(Digest):

它的目的是計算任意長度數據的摘要,這個摘要是固定長度
相同的輸入數據始終得到相同的輸出
不同的輸入數據 儘量 得到不同的輸出
常用語驗證原始數據是否被篡改
Java的 Object.hashCode() 方法就是一個摘要方法,它輸入任意數據,輸出固定長度的數據,輸出的數字實際上是一個 int 類型,可以看成是一個 4字節的 byte 數組。
當兩個不同的輸入得到相同的輸出時,這就叫做 碰撞,碰撞是不能避免的,這是因爲輸入的範圍是無限的,輸出的範圍是有限的。
常用的摘要算法有:

MD5:輸出長度 128bits,即16 個字節 bytes
SHA-1:輸出長度 160bits,即20 個字節 bytes
SHA-256:輸出長度 256bits,即32 個字節 bytes
RipeMD-160:輸出長度 160bits,即20 個字節 bytes
1.2.1 MD5
在 Java 中使用 MD5 需要使用 MessageDigest 這個類,具體步驟:

使用 getInstance("MD5") 這個方法獲取 MessageDigest 關於 MD5 的實例
該實例使用 update(btye[] input) 接受需要加密的字節數組,update 方法可以多次添加字節數組,這和一次添加字節數組是一樣的
最後使用 digest() 方法獲取最終加密好的一個含有16個 btye 的數組,將其轉換爲數字就可以互相比較了,而可以使用 String.format 將其轉化爲 16 進制的數字易於顯示
eg:

String str = "Hello,world";
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(str.getBytes("utf-8"));
byte[] bytes = md5.digest(); // 獲取字節數

// 字節數組轉換爲十六進制數字顯示,其中 `%032x` 是數字顯示格式,`%x`表示使用十六進制顯示數字,`032`表示使用該十六進制的位數爲32個,數字小則開頭自動補零
System.out.println(String.format("%032x", new BigInteger(1, bytes))); // new BigInteger 的第一個參數是表示用大於0


// update 可以分開使用,效果一樣
md5 = null;
md5 = MessageDigest.getInstance("MD5");
md5.update("Hello,".getBytes("utf-8"));
md5.update("world".getBytes("utf-8"));
byte[] bytes2 = md5.digest();
System.out.println(String.format("%032x", new BigInteger(bytes)).equals(String.format("%032x", new BigInteger(bytes2))));


MD5 可常用於驗證數據的完整性,如將登陸密碼儲存在數據庫,避免明文儲存,也常用於查看文件下載是否有缺失。

使用 MD5 儲存密碼時,要預防彩虹表攻擊,即黑客把常用密碼用MD5加密好的數據表,此時可以使用加鹽(通常爲 Secret Key)的方法預防,即在編碼密碼前添加一個字符串
在 MessageDigest.getInstance("MD5") 中將 MD5 改成 SHA-1 就可以改成 SHA-1 算法加密了,需要注意的是 SHA-1 算法的輸出長度是20 個字節 bytes
1.3 對稱加密算法
《Java常用加密技術和代碼總結》

對稱加密算法就是:加密和解密都是用同一個祕鑰,即對稱加密算法在加密的時候需要輸入一個 原文 和一個 密匙 從而得出一個 密文,而解密的時候就需要輸入 密文 和正確的 密匙 從而得到明文。
常用的對稱加密方法有:

DES:祕鑰長度 56/64,工作模式:ECB/CBC/PCBC/CTR/...,填充模式:NoPadding/PKCS5Padding/....(祕鑰過短,短時間能被暴力破解)
AES:祕鑰長度 128/192/256,工作模式:ECB/CBC/PCBC/CTR/...,填充模式:NoPadding/PKCS5Padding/PKCS7Padding/....(使用 256 位祕鑰,需要修改JDK的 policy 文件)
IDEA:祕鑰長度 128,工作模式:ECB,填充模式:PKCS5Padding/PKCS7Padding/....
它們的祕鑰長度各不相同,祕鑰的長度決定了加密的長度,使用對稱加密算法需要指定 算法名稱 / 工作模式 / 填充模式,其中工作模式和填充模式可以看做對稱加密算法的參數和格式的選擇,JDK 內部並沒有包含全部的工作模式和填充模式。

1.3.1 AES(Advanced Encryption Standard)
下例代碼是使用工作模式爲 ECB,填充模式爲 PKCS5Padding 的 AES 方法:

public class About_AES_ECB {

    // 使用 AES 算法,指定工作模式爲 ECB,填充模式爲 PKCS5Padding
    static final String CIPHER_NAME = "AES/ECB/PKCS5Padding"; 
    
    // 加密
    public static byte[] encrypt(byte[] secretKey, byte[] input) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        
        // 使用 Cipher.getInstance() 方法,傳入加密算法的名字,工作模式以及填充模式,從而得到一個 Cipher 實例
        Cipher cipher = Cipher.getInstance(CIPHER_NAME);
        
        // 使用傳入的密匙創建 AES 的 SecretKeySpec 實例
        SecretKeySpec keySpec = new SecretKeySpec(secretKey, "AES");
        
        // cipher 使用 init 方法初始化爲加密模式,並傳入密匙
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        
        // 最後使用 doFinal 傳入明文,得到加密後的密文的字節數組
        return cipher.doFinal(input);
    }
    
    // 解密,僅需要在初始化模式時,初始爲 解密模式即可
    public static byte[] decrypt(byte[] secretKey, byte[] input) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {

        // 使用 Cipher.getInstance() 方法,傳入加密算法的名字,工作模式以及填充模式,從而得到一個 Cipher 實例
        Cipher cipher = Cipher.getInstance(CIPHER_NAME);
        
        // 使用傳入的密匙創建 AES 的 SecretKeySpec 實例
        SecretKeySpec keySpec = new SecretKeySpec(secretKey, "AES");
        
        // cipher 使用 init 方法初始化爲解密模式,並傳入密匙
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        
        // 最後使用 doFinal 傳入明文,得到加密後的密文的字節數組
        return cipher.doFinal(input);
    }
    
    public static void main(String[] args) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
        // 明文
        String input = "Hello.world";
        
        // abs 的密匙長度最短是 128 位,即最短需要 16 個字符
        // 可以換成 192 或 256 位,即 24 個字符或 32 個字符
        String secretKey = "1234567890abcdef";

        System.out.println("加密明文是:" + input + ",祕鑰是:" + secretKey);
        
        // 加密, 注意傳入參數類型是字節數組,注意 utf-8 編碼
        byte[] cipherText = About_AES_ECB.encrypt(secretKey.getBytes("utf-8"),input.getBytes("utf-8"));
        
        // 用 base64 輸出密文字節數組
         System.out.println("base64編碼過後的密文:" + Base64.getEncoder().encodeToString(cipherText));
        
        // 解碼
        String decryptString = new String(About_AES_ECB.decrypt(secretKey.getBytes("utf-8"), cipherText), "utf-8");
        System.out.println("解碼:" + decryptString);
    }
}


1.3.2 PBE(Password Base Encryption)
PBE (口令加密算法)內部使用的仍然是標準對稱加密算法(如 AES),不同在於如 AES 直接生成密匙然後直接使用,而 PBE 是通過 用戶口令 和 隨機salt 計算祕鑰後再進行加密。
如果把隨機 salt 存放在U盤中,就得到了 “口令 + USB key” 的加密軟件,此時即使口令非常弱,沒有 USB key 也無法解密。

1.3.3 密鑰交換算法
在使用對稱加密算法的時候,加解密都是使用同一個密鑰 key,那麼問題是如何在兩個不同的終端傳遞祕鑰?
密鑰交換算法(DH 算法,全稱爲“Diffie-Hellman”),他是一種密鑰交換協議,通信雙方通過 協商 的密鑰,然後進行加密傳輸。如下圖中的 secretKeyA 就是通信雙方協商過後的密鑰,它是由自身的私匙和傳送過來對方的公匙計算得出。

eg:

/*
 *    使用 DH 算法
 *
 */

class Person {
    public Person(String name) {
        this.name = name;
    }
    String name;
    // 公匙
    public PublicKey publicKey;
    // 私匙
    public PrivateKey privateKey;
    // 雙方協商過後通用的密匙,用於 AES 加密的
    public SecretKey secretKey;

    // 生成密匙對
    public void generateKeyPair() throws NoSuchAlgorithmException {

        // 使用 KeyPariGenerator.getInstance 方法,傳入算法 DH
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DH");

        // 表示輸出的密鑰長度是 1024 位
        keyPairGenerator.initialize(512);

        // 獲取密鑰對
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // 獲取私匙
        this.privateKey = keyPair.getPrivate();
        // 獲取公匙
        this.publicKey = keyPair.getPublic();
    }

    // 接受對方公匙,生成密匙
    public void generateSecretKey(byte[] receivedPublicKey)
            throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {

        KeyFactory keyFactory = KeyFactory.getInstance("DH");

        // 將對方發過來的公匙 byte 數組恢復爲 PublicKey,使用 X509EncodedKeySpec
        X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(receivedPublicKey);
        PublicKey receivedPK = keyFactory.generatePublic(pkSpec);

        // 生成本地密匙
        KeyAgreement keyAgreement = KeyAgreement.getInstance("DH");
        // 傳入私匙
        keyAgreement.init(this.privateKey);
        // 對方公匙
        keyAgreement.doPhase(receivedPK, true);
        // 生成本地密鑰,傳入 AES 表示要生成一個 AES 加密的密鑰
        this.secretKey = keyAgreement.generateSecret("AES");
    }

    // 發送加密信息
    public String sendMessage(String message) throws NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {

        // 使用本地密鑰對信息進行 AES 加密
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, this.secretKey);
        byte[] sendData = cipher.doFinal(message.getBytes("utf-8"));

        return Base64.getEncoder().encodeToString(sendData);
    }

    // 解密接受信息
    public String getMessage(String message) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
        
        // 使用本地密鑰對信息進行 AES 接密
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, this.secretKey);
        byte[] getData = cipher.doFinal(Base64.getDecoder().decode(message));

        return new String(getData, "utf-8");
    }
    
    // 輸出相關信息
    public void print() {
        System.out.println("當前類爲:" + this.name);
        System.out.println("私匙爲:" + Base64.getEncoder().encodeToString(this.privateKey.getEncoded()));
        System.out.println("公匙爲:" + Base64.getEncoder().encodeToString(this.publicKey.getEncoded()));
        System.out.println("本地私匙爲:" + Base64.getEncoder().encodeToString(this.secretKey.getEncoded()));
        System.out.println();
    }

}

public class About_DH {

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
        
        // 模擬兩個終端使用 DH 算法
        Person bob = new Person("Bob");
        Person alex = new Person("Alex");
        
        // 生成公匙和私匙
        bob.generateKeyPair();
        alex.generateKeyPair();
        
        // 協議生成本地密匙,傳入對方的公匙,publickey 實例使用 getEncoded 方法可以獲取 byte 數組
        alex.generateSecretKey(bob.publicKey.getEncoded());
        bob.generateSecretKey(alex.publicKey.getEncoded());
        
        // 輸出相關信息,只有本地密匙相同
        alex.print();
        bob.print();
        
        // Alex 向 Bob 發送加密過的密文, Bob 要解密
        String alexToBob = alex.sendMessage("Hello,world");
        System.out.println("Bob解密後的明文:" + bob.getMessage(alexToBob));
    }

}


1.4 非對稱加密算法
非對稱加密就是加密和解密都是用不同的密鑰,分別是公鑰和私鑰。需要注意的一點,這個公鑰和私鑰必須是一對的,如果用公鑰對數據進行加密,那麼只有使用對應的私鑰才能解密,反之亦然。
eg:

public class RSAkeyPair {

    // 公鑰
    PublicKey publicKey;
    // 私鑰
    PrivateKey privateKey;
    
    // 無參構造方法,生成公鑰和私鑰對
    public RSAkeyPair() throws NoSuchAlgorithmException {
        
        // 使用 KeyPariGenerator.getInstance 方法,傳入算法 RSA
        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
        
        // 表示輸出的密鑰長度是 1024 位
        kpGen.initialize(1024);
        
        // 輸出密鑰對
        KeyPair keyPair = kpGen.generateKeyPair();
        
        // 獲取私匙
        this.privateKey = keyPair.getPrivate();
        // 獲取公匙
        this.publicKey = keyPair.getPublic();
    }
    
    // 有參構造函數,通過傳入的密鑰對字節數組恢復密鑰對(讀取保存文件中的密鑰對)
    public RSAkeyPair(byte[] sk, byte[] pk) throws NoSuchAlgorithmException, InvalidKeySpecException {
        
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");

        // 恢復公鑰,使用 X509EncodedKeySpec
        X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(pk);
        this.publicKey = keyFactory.generatePublic(pkSpec);
        
        // 恢復私鑰,使用 PKCS8EncodedKeySpec
        PKCS8EncodedKeySpec skSpec = new PKCS8EncodedKeySpec(sk);
        this.privateKey = keyFactory.generatePrivate(skSpec);
        
    }
    
    // 把私鑰導出爲字節
    public byte[] getSk() {
        return this.privateKey.getEncoded();
    }
    
    // 把公鑰導出爲字節
    public byte[] getPk() {
        return this.publicKey.getEncoded();
    }
    
    // 使用公鑰加密,使用了公匙加密就必須使用私匙解密,否則會報錯
    public byte[] encrypt(byte[] message) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        
        // 創建 Cipher 實例,聲明使用 RSA
        Cipher cipher = Cipher.getInstance("RSA");
        
        // 使用公匙加密
        cipher.init(Cipher.ENCRYPT_MODE, this.publicKey);
        return cipher.doFinal(message);
    }
    
    // 使用私匙解密,使用了公匙加密就必須使用私匙解密,否則會報錯
    public byte[] decrypt(byte[] input) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {

        // 創建 Cipher 實例,聲明使用 RSA
        Cipher cipher = Cipher.getInstance("RSA");
        
        // 使用公匙加密
        cipher.init(Cipher.DECRYPT_MODE, this.privateKey);
        return cipher.doFinal(input);
    }
    
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidKeySpecException {
            String message = "Hello.world!";
            RSAkeyPair keyPair = new RSAkeyPair();
            byte[] cipherText = keyPair.encrypt(message.getBytes("utf-8"));
            System.out.println("加密後使用Base64編碼後的密文:" + Base64.getEncoder().encodeToString(cipherText));
            byte[] decryptText = keyPair.decrypt(cipherText);
            System.out.println("解密後的明文:" + new String(decryptText, "utf-8"));
            
            // 模仿讀取文件密匙對字節,創建密匙對
            byte[] sk = keyPair.getSk();
            byte[] pk = keyPair.getPk();
            
            RSAkeyPair keyPair2 = new RSAkeyPair(sk, pk);
            System.out.println("使用字節流創建的私匙能否解密:" + new String(keyPair2.decrypt(cipherText), "utf-8"));
    }
}


1.4.1 簽名算法
當A和B使用非對稱加密進行數據傳輸,此時如果C擁有B的公鑰,從而冒充A向B發送信息,此時要怎麼辦呢?

使用簽名算法,它是指發送方在發送密文的同時,也發送由自身 私鑰 對原始數據進行簽名過後的簽名數據,接受方可以使用該簽名數據和發送方的 公鑰 進行簽名認證,從而確保該信息是否發送方發出的並且信息有否被篡改(內部大概就是使用了摘要算法確保信息沒有被篡改)。這樣一來發送方使用私鑰對原始數據進行簽名的過程就叫做 數字簽名 。
它的目的:

它防止了不懷好意的人侵略了接受方的服務器並篡改了發送方的公匙爲自己的公匙,從而可以冒充發送方給接收方發送信息,即防僞造發送方
防止發送方抵賴發送過的信息
防止信息在傳送過程中被篡改
常用的方法有: MD5withRSA、SHA1withRSA 、SHA256withRSA

eg:

public class RSASignture {
    
    // 公鑰、私鑰
    PublicKey pk;
    PrivateKey sk;

    // 無參構造方法,生成公鑰和私鑰對
    public RSASignture() throws NoSuchAlgorithmException {
        
        // 使用 KeyPariGenerator.getInstance 方法,傳入算法 RSA
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        
        // 表示輸出的密鑰長度是 1024 位
        keyPairGenerator.initialize(1024);
        
        // 獲取密鑰對,獲取公鑰,私鑰
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        this.pk = keyPair.getPublic();
        this.sk = keyPair.getPrivate();
    }
    
    // 把私鑰導出爲字節
    public byte[] getSk() {
        return this.sk.getEncoded();
    }
    
    // 把公鑰導出爲字節
    public byte[] getPk() {
        return this.pk.getEncoded();
    }
    
    //    定義 sign 方法對數據進行簽名
    public byte[] sign(byte[] message) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        
        // 使用 Signature.getInstance 方法傳入 簽名算法名稱如 SHA1withRSA,獲取 signature 實例
        Signature signature = Signature.getInstance("SHA1withRSA");
        
        // 傳入私匙,初始化簽名
        signature.initSign(this.sk);
        
        // 傳入明文,對明文進行簽名
        signature.update(message);
        
        // 獲得明文簽名後的字節
        return signature.sign();
    }
    
    // 驗證簽名,需要傳入原始信息,驗證數據
    public boolean verify(byte[] message, byte[] sign) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        
        // 使用 Signature.getInstance 方法傳入 簽名算法名稱如 SHA1withRSA,獲取 signature 實例
        Signature signature = Signature.getInstance("SHA1withRSA");
        
        // 傳入公匙初始化簽名,表示要驗證簽名,只能傳入公匙
        signature.initVerify(this.pk);
        
        signature.update(message);
        
        // 驗證簽名
        return signature.verify(sign);
    }
    
    public static void main(String[] args) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        byte[] message = "Hello,world".getBytes("utf-8");
        RSASignture rsaSignture = new RSASignture();
        
        // 獲取簽名
        byte[] sign = rsaSignture.sign(message);
        System.out.println("簽名:" + Base64.getEncoder().encodeToString(sign));
        
        boolean verified = rsaSignture.verify(message, sign);
        System.out.println("驗證結果:" + verified);
        
        // 使用別的公匙驗證
        boolean verified2 = new RSASignture().verify(message, sign);
        System.out.println("使用別的公匙驗證的結果:" + verified2);
        
        // 修改發送信息
        message[0] = 1;
        boolean verified3 = rsaSignture.verify(message, sign);
        System.out.println("修改發送信息後的驗證結果:" + verified3);
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章