淺談基本加密算法以及使用

1.加密算法

(1)對稱加密算法
對稱加密算法中,加密用的密鑰和解密用的密鑰是一樣的,也就是說,加密和解密使用同一個密鑰,密鑰的保存和安全交換是一個問題。對稱加密算法有DES(data encryption standard)數據加密標準,3DES(DESede),AES(Advanced encryption standard)高級加密標準。
(2)單向加密算法
MD4,MD5,SHA192,SHA256,SHA512單向的不可逆的摘要算法,用於驗證數據的完整性。摘要算法的輸出是固定的,192,256指的都是輸出摘要的長度,例如MD5摘要算法就是把一個明文摘要成一個長16字節的結果,只要原文有一點變化,那麼摘要結果就會完全不同(雪崩效應),不能從摘要結果去推到原文。比如Linux系統中的賬號密碼文件就是摘要以後的數據存放進去的,以確保安全。爲了更好的提高安全性,也可以使用加鹽(在明文中添加某些信息,混亂明文以後再進行摘要)的方法。目前MD5,SHA1都被淘汰了,他們的安全性不足,推薦使用SHA256以及以上的算法。
(3)非對稱加密
在非對稱加密算法中,加密使用的密鑰和解密使用的密鑰是不同的。非對稱加密生成一個公鑰和一個私鑰,加密與解密只能在一對私鑰和公鑰之間進行,公鑰加密的私鑰解密。RSA,DSA是兩個非對稱加密算法,用於數字簽名和加密,其中DSA只能用於簽名

2.RSA算法介紹

RSA密碼體制是一種公鑰密碼體制,公鑰公開,私鑰保密,它的加密解密算法是公開的。由公鑰加密的內容可以,並且只能由私鑰解密,並且由私鑰加密的內容可以並且只能由公鑰進行解密。由此可以說,RSA的這一對公鑰,私鑰都可以用來進行加密和解密,並且一方加密的內容只能由對方進行解密,客戶端用公鑰加密的數據,之能是認證的服務端使用相應私鑰解密。這也是這個算法能用於身份識別(數字簽名)的原因。
爲了實現身份認證,RSA體系中的私鑰加密可以實現身份驗證,驗證過程是用私鑰加密,傳給對方,對方用之前獲得的公開的公鑰解密,只要能順利的解開,就能驗證對方的身份是合法的。
要談RSA與完整性的關係。首先要說明什麼是完整性。完整性就是保證傳遞的信息是通信雙方原本想傳遞的內容,沒有被破壞和篡改。簽名保證了完整性。
這裏介紹一下簽名,簽名就是在信息的後面再加上一段內容,可以證明信息沒有被修改過。要想實現該目的,一般是對信息做一個散列計算得到一個hash值,這個過程是不可逆的(使用摘要算法),也就是說,無法通過hash值來推導出原來的明文。把這個hash值加密後作爲一個簽名與信息一同發出,接收方再收到以後,會重新計算信息的hash值,並和信息所附帶的hash值(解密後)進行對比,如果一致,則說明信息的內容沒有被修改過,因爲hash計算可以保證不同的內容一定會得到不同的hash值,所以只要內容一旦被篡改,根據信息內容計算出來的hash值就會改變。
如果不加密的話,攻擊者可以修改信息內容的同時也修改hash值,從而讓它們可以相匹配,爲防止這種情況,hash值一般都會加密後(也就是簽名)再和信息一起發送,以保證這個hash值不被修改。
使用RSA來處理這個簽名,用公鑰加密“明文”+“哈希值”,私鑰解密得到明文。當然,簡單的對稱加密也可以實現完整性的驗證,但是不能實現身份認證。
非對稱加密效率是很低的,比對稱加密低出3個數量級左右。

3.解決對稱加密和非對稱加密缺陷的一種數據傳輸方案

混用對稱加密與非對稱加密,數據加密時使用對稱加密算法,再把對稱加密算法的密鑰和完整性校驗值(hash值),身份認證標識,一起使用RSA密鑰加密,對方接受到之後先使用RSA密鑰解密,得到對稱密鑰,完整性校驗值和身份認證標識,看到對方認證沒問題,再使用對稱密鑰解密密文,並驗證完整性。大致流程圖如下:
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20200611183808220.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmN

4.使用Java中的摘要算法庫對字符串和文件進行摘要測試

public class DigestUtils {

    //這種做法是線程不安全的
    private static Map<String,MessageDigest> map = new HashMap<String ,MessageDigest>();

    public static MessageDigest getMDInstance (String algorithm) {

        try {
            if(map.get(algorithm) == null){
                MessageDigest md = MessageDigest.getInstance(algorithm);
                map.put(algorithm, md);
            }

            return map.get(algorithm);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }


    }
    /**
     * 通過摘要算法將所傳入字符串加密,目前MD5和SHA1已經不安全了。而實際中SHA1加密強度是61位
     * @param str 要加密的字符串
     * @param list 可選摘要算法的列表,包括MD5.SHA1,SHA256,SHA512等算法
     * @return
     */
    public static byte[] encryptByDigest(String str,DigestAlgortihmList list){
        if(str == null || str.length() == 0){
            return null;
        }
        MessageDigest md;
        try{
            //這樣做是線程安全的
            md = MessageDigest.getInstance(list.getName());
            md.update(str.getBytes("utf-8"));
            return md.digest();
        }
        catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 將字節數組轉換爲十六進制字符串
     * @param array
     * @return
     */
    public static String parseToHex(byte[] array){
        if(array == null || array.length == 0){
            return null;
        }

        StringBuffer sb = new StringBuffer();
        for(Byte b : array){
            //System.out.println("b:" + b);
            //System.out.println(Integer.toHexString(b));
            /**
             * 在此對這段代碼進行解釋:b是一個8位的二進制串,以十進制顯示。比如說b是1010 1011
             * 那麼將b右移四位之後變爲0000 1010 然後和0xf進行與操作,爲0000 1010 這是s1
             */
            //高四位轉變爲16進制
            String s1 = Integer.toHexString((b >> 4) & 0xf);
            //System.out.println("s1:" + s1);
            //低四位轉變爲16進制
            String s2 = Integer.toHexString(b & 0xf);
            //System.out.println("s2:" + s2);
            sb.append(s1).append(s2);
        }
        return sb.toString();
    }

    /**
     * 對文件進行摘要,將文件讀爲字節流之後進行摘要
     * @param path 文件路徑
     * @param list 選擇摘要算法
     * @return
     */
    public static String digestOneFile(String path,DigestAlgortihmList list){
        File file = new File(path);
        FileInputStream fin;
        try {
            fin = new FileInputStream(file);
            FileChannel ch = fin.getChannel();  //文件通道
            MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY,0,file.length());//字節緩衝流
            MessageDigest md = MessageDigest.getInstance(list.getName());
            md.update(byteBuffer);
            byte[] result = md.digest();
            return parseToHex(result);
        }
        catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
}

public class TestDigest {
    public static void main(String[] args) throws NoSuchAlgorithmException, CloneNotSupportedException {
        String temp = "178458976218971Hello world你好啊";
        byte[] arr = DigestUtils.encryptByDigest(temp,DigestAlgortihmList.SHA1);
        String result = DigestUtils.parseToHex(arr);
        System.out.println("一段字符串使用SHA1摘要後的結果:" + result);


        //測試線程安全性
//        for(int i=0;i < 100;i ++){
//            Thread thread = new Thread(new Runnable() {
//                @Override
//                public void run() {
//                    String temp = "178458976218971Hello world你好啊";
//                    byte[] arr = DigestUtils.encryptByDigest(temp,DigestAlgortihmList.SHA1);
//                    String result = DigestUtils.parseToHex(arr);
//                    System.out.println(result);
//                }
//            });
//            thread.start();
//        }
        String path = "D:\\apache-tomcat-7.0.52.zip";
        String fileDigestResult = DigestUtils.digestOneFile(path,DigestAlgortihmList.SHA1);
        System.out.println("文件使用SHA1摘要後的結果:" + fileDigestResult);

        MessageDigest md = MessageDigest.getInstance(DigestAlgortihmList.SHA1.getName());
        md.update("178458976218971".getBytes());
        MessageDigest md1 = (MessageDigest)md.clone();  //淺克隆md
        byte[] arr1 = md1.digest();

        md.update("Hello world".getBytes());
        MessageDigest md2 = (MessageDigest)md.clone();
        byte[] arr2 = md2.digest();

        md.update("你好啊".getBytes());
        MessageDigest md3 = (MessageDigest)md.clone();
        byte[] arr3 = md.digest();  //最後調用md復位

        //會發現這樣做完之後是對“178458976218971Hello world你好啊”的累加 與直接進行該字符串累加是一樣的
        System.out.println("累加摘要的測試");
        System.out.println(DigestUtils.parseToHex(arr1));
        System.out.println(DigestUtils.parseToHex(arr2));
        System.out.println(DigestUtils.parseToHex(arr3));
    }
}

在這裏插入圖片描述

5.使用Java中的對稱加密算法庫對字符串進行加密測試

AES算法測試:

public static byte[] encryptByAES128(String str,String random_str) {
        if (str==null || str.length()==0 || random_str==null || random_str.length()==0) {
            return null;
        }
        KeyGenerator kgen;
        try {
            //構建一個密鑰生成器
            kgen = KeyGenerator.getInstance("aes");
            //初始化,將隨機串放入,初始化爲128位
            kgen.init(128, new SecureRandom(random_str.getBytes()));
            SecretKey secretKey = kgen.generateKey();

            System.out.println(secretKey.getEncoded().length);
            //獲取原始密鑰字節數組,new出新的安全密鑰
            SecretKeySpec key = new SecretKeySpec(secretKey.getEncoded(), "AES");

            //創建加密器 指定加密算法  默認模式ECB,有填充 , AES/ECB/PKCS5Padding (128)
            //也可以選擇 AES/ECB/NoPadding 算法是AES 模式是ECB 無填充
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            System.out.println(cipher.getBlockSize());

            //使用CBC模式需要構造一個初始向量
            String str_iv = "8144660571234567";
            IvParameterSpec iv = new IvParameterSpec(str_iv.getBytes("UTF-8"));

            //初始化加密器 選定模式,並將之前準備好的密鑰放進去,如果選擇了CBC模式需要添加進去初始向量
            cipher.init(Cipher.ENCRYPT_MODE, key,iv);
            // xxxx xxxx xxxx xxxx xxxW -> key -->  XX XXXXXXX XX X
            // xxxx^IV -key -> ^xxxx -key->^xxxx -key->  WW WW WWWWW WW
            // 0000    0001          0002    0003    0004
            //         c1(xor v)      c2       c3    c4
            //         enc1          enc2    enc3   enc4
            //  String str_iv = "14725836abcdefgx";
            //   IvParameterSpec iv = new IvParameterSpec(str_iv.getBytes("UTF-8"));
            //    cipher.init(Cipher.ENCRYPT_MODE, key,iv);
            byte[] byteContent = str.getBytes("utf-8");
            return cipher.doFinal(byteContent);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static byte[] dencryptByAES128(byte[] arr,String random_str) {
        if (arr==null || arr.length==0 || random_str==null || random_str.length()==0) {
            return null;
        }
        KeyGenerator kgen;
        try {
            //構建一個密鑰生成器
            kgen = KeyGenerator.getInstance("AES");
            //初始化,將隨機串放入
            kgen.init(128, new SecureRandom(random_str.getBytes()));
            SecretKey secretKey = kgen.generateKey();
            //獲取原始密鑰字節數組,new出新的安全密鑰
            SecretKeySpec key = new SecretKeySpec(secretKey.getEncoded(), "AES");
            //創建加密器 指定加密算法
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            //初始化加密器 選定模式,並將之前準備好的密鑰放進去
            //cipher.init(Cipher.DECRYPT_MODE, key);

            //使用CBC模式需要構造一個初始向量
            String str_iv = "8144660571234567";
            IvParameterSpec iv = new IvParameterSpec(str_iv.getBytes("UTF-8"));
            cipher.init(Cipher.DECRYPT_MODE, key,iv);
            return cipher.doFinal(arr);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
}	
public void testAES() throws UnsupportedEncodingException {
	String str = "123456789Hello world你好";
	String random_str = "echored112240xqezf";
	byte[] result = AESUtils.encryptByAES128(str,random_str);
	System.out.println(HexParseUtils.parseToHex(result));
	String s1 = HexParseUtils.parseToHex(result);
	result = HexParseUtils.parseTobyteArray(s1);
	byte[] result2 = AESUtils.dencryptByAES128(result,random_str);
	System.out.println(new String(result2,"utf-8"));
}

在這裏插入圖片描述
DES算法測試類似,就不再貼出。

6.對稱加密的各種模式

ECB模式:Electronic Code Book Mode 電子密碼本模式,需要對明文進行分組,分組長度可以爲128,256或者512bit。優點:操作簡單,容易實現,每個分組獨立,誤差不被傳送,易於並行。缺點:暴漏明文信息的結構,相同的明文會得到相同的密文。
CBC模式:Cipher Blocking Chaining Mode 加密鏈模式。先將明文分成若干小段,然後每一小段與初始塊(初始值,初始向量,IV)或者上一段密文進行異或運算之後,再用密鑰進行加密。與前面的異或後再加密。優點是:能夠掩蓋明文的結構,也就是相同明文會得出不同的密文,安全性好於ECB,適合傳輸長的明文,是SSL和IPSec的標準。缺點是:無法進行並行運算,有填充,不適合流加密,存在傳遞誤差,一步錯,步步錯。
計算器模式(Counter(CTR)):CTR模式中有一個自增的計數器,將從計數器中得到的數字(參數值)用密鑰加密之後與明文做異或操作得出密文,相當於一次一密。優點是:簡單快速,安全可靠,可並行加密;誤差不會傳遞。缺點是:計數器不易得到。
輸出反饋模式(OFB)
密碼反饋模式(Cipher FeedBack(CFB))
GCM(GMAC Counter Mode):對稱加密採用Counter模式,並附帶有GMAC消息認證碼。GMAC中的G就是指GMAC,C就是指CTR,GCM可以同時完成加密和完整性校驗,另外還可以提供附加消息的完整性校驗。
在實際應用場景中,有些信息是不需要保密的,但是信息的接收者需要確認它的真實性。例如源IP,源端口,目的IP,IV,等等。因此,我們可以將這一部分作爲附加消息,加入到MAC值的計算當中。接收方會收到密文,計數器CTR的初始值,MAC值。其優點時效率高,可並行,誤差不傳播,可同時完成完整性校驗,無填充(適合流加密)

那麼說了一些有關MAC的東西,什麼是MAC呢?MAC(Message Authentication Code 消息驗證碼),它是用來校驗密文完整性的,收發雙方共享一個密鑰。密文發送者將密文的MAC值隨密文一起發送,密文接收者通過密鑰解密收到MAC值。這樣就可以對收到的密文做完整性檢驗。
攻擊者在篡改密文之後,因爲沒有密鑰,也就相應無法計算出篡改後的密文的MAC值。如果加密模式是CTR,或者是其他有初始IV的加密模式,初始的計時器或喫屎向量的值作爲附加消息也與密文一起計算MAC。GMAC(Galois message authentication code,伽羅瓦消息驗證碼),是利用伽羅瓦域乘法運算來計算消息的MAC值。假設密鑰長度爲128bits,當密文大於128bits時,需要將密文按128bits進行分組。

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