Java加密與解密

1. ASCII 編碼

  • ASCII(American Standard Code for Information Interchange,美國(guó)信息交換標(biāo)準(zhǔn)代碼)是基于拉丁字母的一套電腦編碼系統(tǒng),主要用于顯示現(xiàn)代英語(yǔ)和其他西歐語(yǔ)言。它是現(xiàn)今最通用的單字節(jié)編碼系統(tǒng),并等同于國(guó)際標(biāo)準(zhǔn)ISO/IEC 646。


    ascii.jpg
  • 示例代碼

    private static void asciiDemo() {
        char a = 'A';
        int code = a;
        System.out.println(code);
    
        String s = "ABCabc";
        char[] chars = s.toCharArray();
        for (char c : chars) {
            int co = c;
            System.out.println(co);
        }
    }
    

2. 愷撒加密

  • 在密碼學(xué)中,愷撒密碼是一種最簡(jiǎn)單并且最廣為人知的加密技術(shù)。

  • 它是一種替換加密的技術(shù),明文中的所欲字母都在字母表上向后(或向前)按照一個(gè)固定的數(shù)目進(jìn)行偏移后被替換成密文。

  • 例如:當(dāng)偏移量是3的時(shí)候,所有的字母A將被替換成D,B變成E,以此類推。

  • 這個(gè)加密方法是以愷撒的名字命名的,當(dāng)年愷撒曾用此方法與其將軍們進(jìn)行聯(lián)系。

  • 愷撒密碼通常被座位其他更復(fù)雜的加密方法中的一個(gè)步驟。

i002.jpg
  • 示例代碼

    /**
     * 使用凱撒加密方式加密數(shù)據(jù)
     *
     * @param orignal :原文
     * @param key     :密鑰
     * @return :加密后的數(shù)據(jù)
     */
    private static String encryptKaiser(String orignal, int key) {
        // 將字符串轉(zhuǎn)為字符數(shù)組
        char[] chars = orignal.toCharArray();
        StringBuilder sb = new StringBuilder();
        // 遍歷數(shù)組
        for (char aChar : chars) {
            // 獲取字符的ASCII編碼
            int asciiCode = aChar;
            // 偏移數(shù)據(jù)
            asciiCode += key;
            // 將偏移后的數(shù)據(jù)轉(zhuǎn)為字符
            char result = (char) asciiCode;
            // 拼接數(shù)據(jù)
            sb.append(result);
        }
    
        return sb.toString();
    }
    
    /**
     * 使用凱撒加密方式解密數(shù)據(jù)
     *
     * @param encryptedData :密文
     * @param key           :密鑰
     * @return : 源數(shù)據(jù)
     */
    private static String decryptKaiser(String encryptedData, int key) {
        // 將字符串轉(zhuǎn)為字符數(shù)組
        char[] chars = encryptedData.toCharArray();
        StringBuilder sb = new StringBuilder();
        // 遍歷數(shù)組
        for (char aChar : chars) {
            // 獲取字符的ASCII編碼
            int asciiCode = aChar;
            // 偏移數(shù)據(jù)
            asciiCode -= key;
            // 將偏移后的數(shù)據(jù)轉(zhuǎn)為字符
            char result = (char) asciiCode;
            // 拼接數(shù)據(jù)
            sb.append(result);
        }
    
        return sb.toString();
    }
    

2.1頻度分析法破解愷撒加密

  • 將明文字母的出現(xiàn)頻率與密文字母的頻率相比較的過(guò)程
  • 通過(guò)分析每個(gè)符號(hào)出現(xiàn)的頻率而輕易地破譯代換式密碼
  • 在每種語(yǔ)言中,冗長(zhǎng)的文章中的字母表現(xiàn)出一種可對(duì)之進(jìn)行分辨的頻率。
  • e是英語(yǔ)中最常用的字母,其出現(xiàn)頻率為八分之一

3. Byte 和bit

  • Byte:字節(jié)。數(shù)據(jù)存儲(chǔ)的基本單位。

  • bit:比特,又叫位。一個(gè)位要么是0,要么是1.數(shù)據(jù)傳輸?shù)膯挝弧?/p>

  • 關(guān)系:1Byte = 8bit

  • 示例代碼:

    // 中文在GBK編碼下, 占據(jù)2個(gè)字節(jié)
    // 中文在UTF-8編碼下, 占據(jù)3個(gè)字節(jié)
    private static void getChinese() throws UnsupportedEncodingException {
        String a = "我";
        byte[] bytes = a.getBytes("UTF-8");
        for (byte b : bytes) {
            // 獲取字節(jié)
            System.out.print(b + "   ");
            // 獲取位
            String s = Integer.toBinaryString(b);
            System.out.println(s);
        }
    }
    
    // 英文在GBK和UTF-8編碼下,始終占據(jù)一個(gè)字節(jié)
    private static void getEnglish() throws UnsupportedEncodingException {
        String a = "A";
        byte[] bytes = a.getBytes("GBK");
        for (byte b : bytes) {
            // 獲取字節(jié)
            System.out.print(b + "   ");
            // 獲取位
            String s = Integer.toBinaryString(b);
            System.out.println(s);
        }
    }
    

常見(jiàn)加密方式

i003.png

4. 對(duì)稱加密

  • 采用單鑰密碼系統(tǒng)的加密方法,同一個(gè)密鑰可以同時(shí)用作信息的加密和解密,這種加密方法稱為對(duì)稱加密,也稱為單密鑰加密。
  • 示例
    • 我們現(xiàn)在有一個(gè)原文3要發(fā)送給B
    • 設(shè)置密鑰為108, 3 * 108 = 324, 將324作為密文發(fā)送給B
    • B拿到密文324后, 使用324/108 = 3 得到原文
  • 常見(jiàn)加密算法
    • DES : Data Encryption Standard,即數(shù)據(jù)加密標(biāo)準(zhǔn),是一種使用密鑰加密的塊算法,1977年被美國(guó)聯(lián)邦政府的國(guó)家標(biāo)準(zhǔn)局確定為聯(lián)邦資料處理標(biāo)準(zhǔn)(FIPS),并授權(quán)在非密級(jí)政府通信中使用,隨后該算法在國(guó)際上廣泛流傳開(kāi)來(lái)。
    • AES : Advanced Encryption Standard, 高級(jí)加密標(biāo)準(zhǔn) .在密碼學(xué)中又稱Rijndael加密法,是美國(guó)聯(lián)邦政府采用的一種區(qū)塊加密標(biāo)準(zhǔn)。這個(gè)標(biāo)準(zhǔn)用來(lái)替代原先的DES,已經(jīng)被多方分析且廣為全世界所使用。
  • 特點(diǎn)
    • 加密速度快, 可以加密大文件
    • 密文可逆, 一旦密鑰文件泄漏, 就會(huì)導(dǎo)致數(shù)據(jù)暴露
    • 加密后編碼表找不到對(duì)應(yīng)字符, 出現(xiàn)亂碼
    • 一般結(jié)合Base64使用

5. 非對(duì)稱加密

  • 示例

    • 首先生成密鑰對(duì), 公鑰為(5,14), 私鑰為(11,14)
    • 現(xiàn)在A希望將原文2發(fā)送給B
    • A使用公鑰加密數(shù)據(jù). 2的5次方mod 14 = 4 , 將密文4發(fā)送給B
    • B使用私鑰解密數(shù)據(jù). 4的11次方mod14 = 2, 得到原文2
  • 特點(diǎn)

    • 加密和解密使用不同的密鑰
    • 如果使用私鑰加密, 只能使用公鑰解密
    • 如果使用公鑰加密, 只能使用私鑰解密
    • 處理數(shù)據(jù)的速度較慢, 因?yàn)榘踩?jí)別高
  • 常見(jiàn)算法

    • RSA
    • ECC
  • 示例代碼

    import com.sun.org.apache.xml.internal.security.utils.Base64;
    import org.apache.commons.io.FileUtils;
    import javax.crypto.Cipher;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.nio.charset.Charset;
    import java.security.*;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.security.spec.X509EncodedKeySpec;
    
    public class RsaDemo {
    
        public static void main(String[] args) {
            String algorithm = "RSA";
            String input = "非對(duì)稱加密與對(duì)稱加密相比,其安全性更好:對(duì)稱加密的通信雙方使用相同的秘鑰,如果一方的秘鑰遭泄露,那么整個(gè)通信就會(huì)被破解。而非對(duì)稱加密使用一對(duì)秘鑰,一個(gè)用來(lái)加密,一個(gè)用來(lái)解密,而且公鑰是公開(kāi)的,秘鑰是自己保存的,不需要像對(duì)稱加密那樣在通信之前要先同步秘鑰";
            try {
                generateKeyToFile(algorithm, "a.pub", "a.pri");
    
                PublicKey publicKey = loadPublicKeyFromFile(algorithm, "a.pub");
                PrivateKey privateKey = loadPrivateKeyFromFile(algorithm, "a.pri");
    
                String encrypt = encrypt(algorithm, input, privateKey, 245);
                String decrypt = decrypt(algorithm, encrypt, publicKey, 256);
    
                System.out.println(decrypt);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 生成密鑰對(duì)并保存在本地文件中
         *
         * @param algorithm : 算法
         * @param pubPath   : 公鑰保存路徑
         * @param priPath   : 私鑰保存路徑
         * @throws Exception
         */
        private static void generateKeyToFile(String algorithm, String pubPath, String priPath) throws Exception {
            // 獲取密鑰對(duì)生成器
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
            // 獲取密鑰對(duì)
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            // 獲取公鑰
            PublicKey publicKey = keyPair.getPublic();
            // 獲取私鑰
            PrivateKey privateKey = keyPair.getPrivate();
            // 獲取byte數(shù)組
            byte[] publicKeyEncoded = publicKey.getEncoded();
            byte[] privateKeyEncoded = privateKey.getEncoded();
            // 進(jìn)行Base64編碼
            String publicKeyString = Base64.encode(publicKeyEncoded);
            String privateKeyString = Base64.encode(privateKeyEncoded);
            // 保存文件
            FileUtils.writeStringToFile(new File(pubPath), publicKeyString, Charset.forName("UTF-8"));
            FileUtils.writeStringToFile(new File(priPath), privateKeyString, Charset.forName("UTF-8"));
    
        }
    
        /**
         * 從文件中加載公鑰
         *
         * @param algorithm : 算法
         * @param filePath  : 文件路徑
         * @return : 公鑰
         * @throws Exception
         */
        private static PublicKey loadPublicKeyFromFile(String algorithm, String filePath) throws Exception {
            // 將文件內(nèi)容轉(zhuǎn)為字符串
            String keyString = FileUtils.readFileToString(new File(filePath), Charset.forName("UTF-8"));
    
            return loadPublicKeyFromString(algorithm, keyString);
    
        }
    
        /**
         * 從字符串中加載公鑰
         *
         * @param algorithm : 算法
         * @param keyString : 公鑰字符串
         * @return : 公鑰
         * @throws Exception
         */
        private static PublicKey loadPublicKeyFromString(String algorithm, String keyString) throws Exception {
            // 進(jìn)行Base64解碼
            byte[] decode = Base64.decode(keyString);
            // 獲取密鑰工廠
            KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
            // 構(gòu)建密鑰規(guī)范
            X509EncodedKeySpec keyspec = new X509EncodedKeySpec(decode);
            // 獲取公鑰
            return keyFactory.generatePublic(keyspec);
    
        }
    
        /**
         * 從文件中加載私鑰
         *
         * @param algorithm : 算法
         * @param filePath  : 文件路徑
         * @return : 私鑰
         * @throws Exception
         */
        private static PrivateKey loadPrivateKeyFromFile(String algorithm, String filePath) throws Exception {
            // 將文件內(nèi)容轉(zhuǎn)為字符串
            String keyString = FileUtils.readFileToString(new File(filePath), Charset.forName("UTF-8"));
            return loadPrivateKeyFromString(algorithm, keyString);
    
        }
    
        /**
         * 從字符串中加載私鑰
         *
         * @param algorithm : 算法
         * @param keyString : 私鑰字符串
         * @return : 私鑰
         * @throws Exception
         */
        private static PrivateKey loadPrivateKeyFromString(String algorithm, String keyString) throws Exception {
            // 進(jìn)行Base64解碼
            byte[] decode = Base64.decode(keyString);
            // 獲取密鑰工廠
            KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
            // 構(gòu)建密鑰規(guī)范
            PKCS8EncodedKeySpec keyspec = new PKCS8EncodedKeySpec(decode);
            // 生成私鑰
            return keyFactory.generatePrivate(keyspec);
    
        }
    
        /**
         * 使用密鑰加密數(shù)據(jù)
         *
         * @param algorithm      : 算法
         * @param input          : 原文
         * @param key            : 密鑰
         * @param maxEncryptSize : 最大加密長(zhǎng)度(需要根據(jù)實(shí)際情況進(jìn)行調(diào)整)
         * @return : 密文
         * @throws Exception
         */
        private static String encrypt(String algorithm, String input, Key key, int maxEncryptSize) throws Exception {
            // 獲取Cipher對(duì)象
            Cipher cipher = Cipher.getInstance(algorithm);
            // 初始化模式(加密)和密鑰
            cipher.init(Cipher.ENCRYPT_MODE, key);
            // 將原文轉(zhuǎn)為byte數(shù)組
            byte[] data = input.getBytes();
            // 總數(shù)據(jù)長(zhǎng)度
            int total = data.length;
            // 輸出流
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            decodeByte(maxEncryptSize, cipher, data, total, baos);
            // 對(duì)密文進(jìn)行Base64編碼
            return Base64.encode(baos.toByteArray());
    
        }
    
        /**
         * 解密數(shù)據(jù)
         *
         * @param algorithm      : 算法
         * @param encrypted      : 密文
         * @param key            : 密鑰
         * @param maxDecryptSize : 最大解密長(zhǎng)度(需要根據(jù)實(shí)際情況進(jìn)行調(diào)整)
         * @return : 原文
         * @throws Exception
         */
        private static String decrypt(String algorithm, String encrypted, Key key, int maxDecryptSize) throws Exception {
            // 獲取Cipher對(duì)象
            Cipher cipher = Cipher.getInstance(algorithm);
            // 初始化模式(解密)和密鑰
            cipher.init(Cipher.DECRYPT_MODE, key);
            // 由于密文進(jìn)行了Base64編碼, 在這里需要進(jìn)行解碼
            byte[] data = Base64.decode(encrypted);
            // 總數(shù)據(jù)長(zhǎng)度
            int total = data.length;
            // 輸出流
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
    
            decodeByte(maxDecryptSize, cipher, data, total, baos);
            // 輸出原文
            return baos.toString();
    
        }
    
        /**
         * 分段處理數(shù)據(jù)
         *
         * @param maxSize : 最大處理能力
         * @param cipher  : Cipher對(duì)象
         * @param data    : 要處理的byte數(shù)組
         * @param total   : 總數(shù)據(jù)長(zhǎng)度
         * @param baos    : 輸出流
         * @throws Exception
         */
        private static void decodeByte(int maxSize, Cipher cipher, byte[] data, int total, ByteArrayOutputStream baos) throws Exception {
            // 偏移量
            int offset = 0;
            // 緩沖區(qū)
            byte[] buffer;
            // 如果數(shù)據(jù)沒(méi)有處理完, 就一直繼續(xù)
            while (total - offset > 0) {
                // 如果剩余的數(shù)據(jù) >= 最大處理能力, 就按照最大處理能力來(lái)加密數(shù)據(jù)
                if (total - offset >= maxSize) {
                    // 加密數(shù)據(jù)
                    buffer = cipher.doFinal(data, offset, maxSize);
                    // 偏移量向右側(cè)偏移最大數(shù)據(jù)能力個(gè)
                    offset += maxSize;
                } else {
                    // 如果剩余的數(shù)據(jù) < 最大處理能力, 就按照剩余的個(gè)數(shù)來(lái)加密數(shù)據(jù)
                    buffer = cipher.doFinal(data, offset, total - offset);
                    // 偏移量設(shè)置為總數(shù)據(jù)長(zhǎng)度, 這樣可以跳出循環(huán)
                    offset = total;
                }
                // 向輸出流寫(xiě)入數(shù)據(jù)
                baos.write(buffer);
            }
        }
    
    }
    

6. DES加密解密

  • 示例代碼

    import com.sun.org.apache.xml.internal.security.utils.Base64;
    import javax.crypto.Cipher;
    import javax.crypto.SecretKey;
    import javax.crypto.SecretKeyFactory;
    import javax.crypto.spec.DESKeySpec;
    import javax.crypto.spec.IvParameterSpec;
    import java.security.spec.KeySpec;
    
    public class DESDemo {
    
        public static final String algorithm = "DES";
    
        // 這是默認(rèn)模式
        // public static final String transformation = "DES/ECB/PKCS5Padding";
        // 使用CBC模式, 在初始化Cipher對(duì)象時(shí), 需要增加參數(shù), 初始化向量IV : IvParameterSpec iv = new IvParameterSpec(key.getBytes());
        // public static final String transformation = "DES/CBC/PKCS5Padding";
        // NOPadding: 使用NOPadding模式時(shí), 原文長(zhǎng)度必須是8byte的整數(shù)倍
        public static final String transformation = "DES/ECB/NOPadding";
        public static final String key = "12345678";
        public static final String original = "你好11";
    
        // QO2klVpoYT8=
        // QO2klVpoYT8=
        public static void main(String[] args) throws Exception {
    
            String encryptByDES = encryptByDES();
    
            System.out.println(encryptByDES);
            String decryptByDES = decryptByDES(encryptByDES);
            System.out.println(decryptByDES);
        }
    
        public static String encryptByDES() throws Exception {
            // 獲取Cipher
            Cipher cipher = Cipher.getInstance(transformation);
    
          // 指定密鑰規(guī)則
          SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
    
            // 指定模式(加密)和密鑰
            // 創(chuàng)建初始向量
            IvParameterSpec iv = new IvParameterSpec(key.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
            //  cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv);
            // 加密
            byte[] bytes = cipher.doFinal(original.getBytes());
            // 輸出加密后的數(shù)據(jù)
            // com.sun.org.apache.xml.internal.security.utils.Base64
            return Base64.encode(bytes);
        }
    
        public static String decryptByDES(String encrypted) throws Exception {
            // 獲取Cipher
            Cipher cipher = Cipher.getInstance(transformation);
    
          // 指定密鑰規(guī)則
          SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
    
            // 指定模式(解密)和密鑰
            // 創(chuàng)建初始向量
            IvParameterSpec iv = new IvParameterSpec(key.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            //  cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, iv);
            // 解碼密文
            // com.sun.org.apache.xml.internal.security.utils.Base64
            byte[] decode = Base64.decode(encrypted);
            // 解密
            byte[] bytes = cipher.doFinal(decode);
            // 輸出解密后的數(shù)據(jù)
            return new String(bytes);
        }
    
    }
    

7. AES加密解密

  • 示例代碼

    import com.sun.org.apache.xml.internal.security.utils.Base64;
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    
    public class AESDemo {
    
        public static final String algorithm = "AES";
    
        // 這是默認(rèn)模式
        //  public static final String transformation = "AES/ECB/PKCS5Padding";
        // 使用CBC模式, 在初始化Cipher對(duì)象時(shí), 需要增加參數(shù), 初始化向量IV : IvParameterSpec iv = new IvParameterSpec(key.getBytes());
        //  public static final String transformation = "AES/CBC/PKCS5Padding";
        // NOPadding: 使用NOPadding模式時(shí), 原文長(zhǎng)度必須是8byte的整數(shù)倍
        public static final String transformation = "AES/CBC/NOPadding";
        public static final String key = "1234567812345678";
        public static final String original = "你好你好你1";
    
        public static void main(String[] args) throws Exception {
    
            String encryptByAES = encryptByAES();
            System.out.println(encryptByAES);
            String decryptByAES = decryptByAES(encryptByAES);
            System.out.println(decryptByAES);
    
        }
    
        public static String encryptByAES() throws Exception {
    
            // 獲取Cipher
            Cipher cipher = Cipher.getInstance(transformation);
            // 生成密鑰
            SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), algorithm);
            // 指定模式(加密)和密鑰
            // 創(chuàng)建初始化向量
            IvParameterSpec iv = new IvParameterSpec(key.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
            //cipher.init(Cipher.ENCRYPT_MODE, keySpec);
            // 加密
            byte[] bytes = cipher.doFinal(original.getBytes());
    
            return Base64.encode(bytes);
        }
    
        public static String decryptByAES(String encrypted) throws Exception {
    
            // 獲取Cipher
            Cipher cipher = Cipher.getInstance(transformation);
            // 生成密鑰
            SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), algorithm);
            // 指定模式(解密)和密鑰
            // 創(chuàng)建初始化向量
            IvParameterSpec iv = new IvParameterSpec(key.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
            //  cipher.init(Cipher.DECRYPT_MODE, keySpec);
            // 解密
            byte[] bytes = cipher.doFinal(Base64.decode(encrypted));
    
            return new String(bytes);
        }
    }
    

7.1 加密模式 - ECB

  • ECB : Electronic codebook, 電子密碼本. 需要加密的消息按照塊密碼的塊大小被分為數(shù)個(gè)塊,并對(duì)每個(gè)塊進(jìn)行獨(dú)立加密


    i004.png
  • 優(yōu)點(diǎn) : 可以并行處理數(shù)據(jù)
  • 缺點(diǎn) : 同樣的原文生成同樣的密文, 不能很好的保護(hù)數(shù)據(jù)

7.2 加密模式 - CBC

  • CBC : Cipher-block chaining, 密碼塊鏈接. 每個(gè)明文塊先與前一個(gè)密文塊進(jìn)行異或后,再進(jìn)行加密。在這種方法中,每個(gè)密文塊都依賴于它前面的所有明文塊


    i005.png
  • 優(yōu)點(diǎn) : 同樣的原文生成的密文不一樣
  • 缺點(diǎn) : 串行處理數(shù)據(jù)

7.3 填充模式 - NoPadding

  • 不填充.
  • 在DES加密算法下, 要求原文長(zhǎng)度必須是8byte的整數(shù)倍
  • 在AES加密算法下, 要求原文長(zhǎng)度必須是16byte的整數(shù)倍

7.4 填充模式 - PKCS5Padding

8. 消息摘要

  • 消息摘要(Message Digest)又稱為數(shù)字摘要(Digital Digest)
  • 它是一個(gè)唯一對(duì)應(yīng)一個(gè)消息或文本的固定長(zhǎng)度的值,它由一個(gè)單向Hash加密函數(shù)對(duì)消息進(jìn)行作用而產(chǎn)生

8.1 特點(diǎn):

  • 無(wú)論輸入的消息有多長(zhǎng),計(jì)算出來(lái)的消息摘要的長(zhǎng)度總是固定的。例如應(yīng)用MD5算法摘要的消息有128個(gè)比特位,用SHA-1算法摘要的消息最終有160比特位的輸出

  • 只要輸入的消息不同,對(duì)其進(jìn)行摘要以后產(chǎn)生的摘要消息也必不相同;但相同的輸入必會(huì)產(chǎn)生相同的輸出

  • 消息摘要是單向、不可逆的

  • 常見(jiàn)算法 :

    • MD5
    • SHA1
    • SHA256
    • SHA512
  • 在線獲取消息摘要

  • 示例代碼

    import java.io.ByteArrayOutputStream;
    import java.io.FileInputStream;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    
    public class Md5Demo {
    
        public static void main(String[] args) throws Exception {
            String input = "aa";
    
            String md5 = getDigestFile("MD5", "apache-tomcat-9.0.8-windows-x64.zip");
            System.out.println(md5);
    
            String sha1 = getDigestFile("SHA-1", "apache-tomcat-9.0.8-windows-x64.zip");
            System.out.println(sha1);
    
            String sha256 = getDigestFile("SHA-256", "apache-tomcat-9.0.8-windows-x64.zip");
            System.out.println(sha256);
    
            String sha512 = getDigestFile("SHA-512", "apache-tomcat-9.0.8-windows-x64.zip");
            System.out.println(sha512);
    
        }
    
        /**
         * 獲取字符串的消息摘要
         *
         * @param algorithm : 算法
         * @param input     : 原文
         * @return : 消息摘要
         * @throws NoSuchAlgorithmException
         */
        private static String getDigest(String algorithm, String input) throws NoSuchAlgorithmException {
            // 獲取消息摘要對(duì)象
            MessageDigest md = MessageDigest.getInstance(algorithm);
            // 獲取消息摘要
            byte[] digest = md.digest(input.getBytes());
    
            return toHex(digest);
        }
    
        /**
         * 獲取文件的消息摘要
         *
         * @param algorithm : 算法
         * @param filePath  : 文件路徑
         * @return : 消息摘要
         * @throws Exception
         */
        private static String getDigestFile(String algorithm, String filePath) throws Exception {
            FileInputStream fis = new FileInputStream(filePath);
            int len;
            byte[] buffer = new byte[1024];
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            while ((len = fis.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
    
            // 獲取消息摘要對(duì)象
            MessageDigest md = MessageDigest.getInstance(algorithm);
            // 獲取消息摘要
            byte[] digest = md.digest(baos.toByteArray());
    
            return toHex(digest);
    
        }
    
        private static String toHex(byte[] digest) {
            StringBuilder sb = new StringBuilder();
    
            for (byte b : digest) {
                // 轉(zhuǎn)為16jinzhi進(jìn)制數(shù)據(jù)
                int i = b & 0xff;
                // 轉(zhuǎn)為字符串
                String hex = Integer.toHexString(i);
                // 如果長(zhǎng)度為1,前面補(bǔ)0
                if (hex.length() == 1) {
                    hex = 0 + hex;
                }
                sb.append(hex);
            }
            return sb.toString();
        }
    }
    

8.2 總結(jié)

  • MD5算法 : 摘要結(jié)果16個(gè)字節(jié), 轉(zhuǎn)16進(jìn)制后32個(gè)字節(jié)
  • SHA1算法 : 摘要結(jié)果20個(gè)字節(jié), 轉(zhuǎn)16進(jìn)制后40個(gè)字節(jié)
  • SHA256算法 : 摘要結(jié)果32個(gè)字節(jié), 轉(zhuǎn)16進(jìn)制后64個(gè)字節(jié)
  • SHA512算法 : 摘要結(jié)果64個(gè)字節(jié), 轉(zhuǎn)16進(jìn)制后128個(gè)字節(jié)

9.數(shù)字簽名

  • 數(shù)字簽名又稱公鑰數(shù)字簽名、電子簽章

  • 就是只有信息的發(fā)送者才能產(chǎn)生的別人無(wú)法偽造的一段數(shù)字串,這段數(shù)字串同時(shí)也是對(duì)信息的發(fā)送者發(fā)送信息真實(shí)性的一個(gè)有效證明

  • 數(shù)字簽名是非對(duì)稱密鑰加密技術(shù)與數(shù)字摘要技術(shù)的應(yīng)用

  • 示例代碼

    import com.sun.org.apache.xml.internal.security.utils.Base64;
    import java.security.*;
    
    public class SignatureDemo {
        public static void main(String[] args) throws Exception {
            String a = "123";
    
            PublicKey publicKey = RsaDemo.loadPublicKeyFromFile("RSA", "a.pub");
            PrivateKey privateKey = RsaDemo.loadPrivateKeyFromFile("RSA", "a.pri");
    
            String signaturedData = getSignature(a, "sha256withrsa", privateKey);
    
            boolean b = verifySignature(a, "sha256withrsa", publicKey, signaturedData);
    
        }
    
        /**
         * 生成簽名
         *
         * @param input      : 原文
         * @param algorithm  : 算法
         * @param privateKey : 私鑰
         * @return : 簽名
         * @throws Exception
         */
        private static String getSignature(String input, String algorithm, PrivateKey privateKey) throws Exception {
            // 獲取簽名對(duì)象
            Signature signature = Signature.getInstance(algorithm);
            // 初始化簽名
            signature.initSign(privateKey);
            // 傳入原文
            signature.update(input.getBytes());
            // 開(kāi)始簽名
            byte[] sign = signature.sign();
            // 對(duì)簽名數(shù)據(jù)進(jìn)行Base64編碼
            return Base64.encode(sign);
        }
    
        /**
         * 校驗(yàn)簽名
         *
         * @param input          : 原文
         * @param algorithm      : 算法
         * @param publicKey      : 公鑰
         * @param signaturedData : 簽名
         * @return : 數(shù)據(jù)是否被篡改
         * @throws Exception
         */
        private static boolean verifySignature(String input, String algorithm, PublicKey publicKey, String signaturedData) throws Exception {
            // 獲取簽名對(duì)象
            Signature signature = Signature.getInstance(algorithm);
            // 初始化簽名
            signature.initVerify(publicKey);
            // 傳入原文
            signature.update(input.getBytes());
            // 校驗(yàn)數(shù)據(jù)
            return signature.verify(Base64.decode(signaturedData));
    
        }
    
    }
    

9.1簽名流程圖

i006.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,401評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,011評(píng)論 3 413
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,263評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,543評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,323評(píng)論 6 404
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,874評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,968評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,095評(píng)論 0 286
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,605評(píng)論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,551評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,720評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,242評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,961評(píng)論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,358評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,612評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,330評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,690評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容

  • 概述 之前一直對(duì)加密相關(guān)的算法知之甚少,只知道類似DES、RSA等加密算法能對(duì)數(shù)據(jù)傳輸進(jìn)行加密,且各種加密算法各有...
    Henryzhu閱讀 3,040評(píng)論 0 14
  • Base64.java public final class Base64 { static private ...
    BUG弄潮兒閱讀 808評(píng)論 0 0
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,775評(píng)論 18 139
  • package com.duoduozb.constants; import java.io.ByteArrayO...
    deli6780閱讀 3,394評(píng)論 0 1
  • 今天我要寫(xiě)故事,確切的說(shuō)是寫(xiě)我與學(xué)生的故事。既然是故事,就有可能是生活中提煉出來(lái)的虛虛實(shí)實(shí),難以確定。而...
    Meridianline閱讀 1,125評(píng)論 16 30