java中的加密算法

數據的加密,在數據傳輸過程中是常用到的,前面幾天在公衆號上看到一篇文章講的加密算法,感覺寫得很不錯,於是乎記錄寫自己感覺重要的再加上寫自己的理解並粘上代碼。

一.什麼是加密算法
數據加密的基本過程就是對原來爲明文的文件或數據按某種算法進行處理,使其成爲不可讀的一段代碼,通常稱爲“密文”,使其只能在輸入相應的密鑰之後才能顯示出本來內容,通過這樣的途徑來達到保護數據不被非法人竊取,閱讀的目的。該過程的逆過程爲解密,即將該編碼信息轉化爲其原始數據的過程.Java的加密知識也是Java的常見的領域之一,加密技術的底層確實很複雜,運用了大量的數學知識,要弄明白非常複雜。但是Java的語言中運用密碼加密工具卻是非常簡單。我們在Java的裏面運用這些加密技術,只需要把原理和使用場景等搞明白就可以了,具體底層實現不用研究。

二,分類

常用的加密算法有對稱加密算法,非對稱加密算法,哈希算法,數字簽名等幾類。    

對稱加密顧名思義就是加密和解密是對稱的,加密時用一個祕鑰去加密,解密時用同一個祕鑰去解密,由信息發送方和接收方共同約定一個祕鑰。缺點是風險都在這個祕鑰上面,一旦被竊取,信息會暴露。所以安全級別不夠高。常用對稱加密算法有DES,3DES,AES等。在JDK中也都有封裝。

非對稱加密,顧名思義就是加密與解密的過程不是對稱的,不是用的同一個祕鑰。非對稱加密有個公私鑰對的概念,也就是有兩把祕鑰,一把是公鑰,一把是私鑰,一對公私鑰有固定的生成方法,在加密的時候,用公鑰去加密,接收方再用對應的私鑰去解密。使用時可以由接收方生成公私鑰對,然後將公鑰傳給加密方,這樣私鑰不會在網絡中傳輸,沒有被竊取的風險。比如GitHub的底層的SSH協議就是公私鑰非對稱加密。並且公鑰是可以由私鑰推導出來的,反過來卻不行,由通過公鑰無法推導出私鑰。常用算法有RSA,ECC等.ECC也是比特幣底層用的比較多的算法。通過和對稱加密的對比,可以看到,非對稱加密解決了祕鑰傳輸中的安全問題。

哈希算法,簡單說就是將任意數據都轉換成一個固定長度的字符串。通過哈希後的值幾乎無法推導出原文。而且兩個不同的原文哈希後結果一定不同。常用算法有MD5, SHA256等等。常用場景,MD5常用場景是數據庫的密碼存儲.sha256在挖礦中可以用到。

非對稱加密也有一個問題,就是內容在發送前可能被篡改,因爲公鑰是有可能被竊取的,所以竊取者完全可以改爲發送別的內容。

解決的辦法就是數字簽名。數字簽名和非對稱加密是反過來的,也是有公私鑰對,但是是用私鑰簽名,用公鑰去驗證簽名。比如發送方除了發送用公鑰加密後的密文,還要發送簽名,簽名內容通常是密文哈希後的字符串,接收方首先驗證簽名是否正確,如果正確那麼密文解密後就是真正需要並且沒有被篡改過的內容。注意,簽名和非對稱用的是兩對不同的公私鑰。

上面是對幾個加密算法的一個簡單講解,除了上面的還有base58等,比特幣底層安全也是依賴於加密。後面會一個一個介紹要用到的加密算法的介紹和使用,但是僅僅是使用,底層不會講。

三,算法詳解
 

MD5

 

MD5即消息摘要算法5(信息 - 摘要算法5),用於確保信息傳輸完整一致。是計算機廣泛使用的雜湊算法之一(又譯摘要算法,哈希算法),主流編程語言普遍已有MD5實現。將數據(如漢字)運算爲另一固定長度值,是雜湊算法的基礎原理,MD5的前身有MD2,MD3和MD4。

MD5算法具有以下特點:

1,壓縮性:任意長度的數據,算出的MD5值長度都是固定的。

2,容易計算:從原數據計算出MD5值很容易。

3,抗修改性:對原數據進行任何改動,哪怕只修改1個字節,所得到的MD5值都有很大區別。

4,強抗碰撞:已知原數據和其MD5值,想找到一個具有相同MD5值的數據(即僞造數據)是非常困難的。

MD5的作用是讓大容量信息在用數字簽名軟件簽署私人密鑰前被"壓縮"成一種保密的格式(就是把一個任意長度的字節串變換成一定長的十六進制數字串)。除了MD5以外,其中比較有名的還有sha-1、RIPEMD以及Haval等。

JDK就自帶了md5加密算法,直接調用很方便。需要引入一個類:
import java.security.MessageDigest;
具體代碼如下(直接copy就可以使用):

import java.security.MessageDigest;

/**
 * @version :2018年3月11日 上午10:35:12 類說明
 */
public class MD5Util {

	public static String bytesToHexString(byte[] src) {
		StringBuilder stringBuilder = new StringBuilder("");
		if (src == null || src.length <= 0) {
			return null;
		}
		for (int i = 0; i < src.length; i++) {
			int v = src[i] & 0xFF;
			String hv = Integer.toHexString(v);
			if (hv.length() < 2) {
				stringBuilder.append(0);
			}
			stringBuilder.append(hv);
		}
		return stringBuilder.toString();
	}

	/**
	 * 解析
	 * 
	 * @param hexString
	 * @return
	 */
	public static byte[] hexStringToBytes(String hexString) {
		if (hexString == null || hexString.equals("")) {
			return null;
		}
		hexString = hexString.toUpperCase();
		int length = hexString.length() / 2;
		char[] hexChars = hexString.toCharArray();
		byte[] d = new byte[length];
		for (int i = 0; i < length; i++) {
			int pos = i * 2;
			d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
		}
		return d;
	}

	/**
	 * 將指定byte數組以16進制的形式打印到控制檯
	 * 
	 * @param b
	 */
	public static void printHexString(byte[] b) {
		for (int i = 0; i < b.length; i++) {
			String hex = Integer.toHexString(b[i] & 0xFF);
			if (hex.length() == 1) {
				hex = '0' + hex;
			}
			System.out.print(hex.toUpperCase());
		}

	}

	/**
	 * Convert char to byte
	 * 
	 * @param c
	 *            char
	 * @return byte
	 */
	private static byte charToByte(char c) {
		return (byte) "0123456789abcdef".indexOf(c);
	}

	/**
	 * 加密
	 * 
	 * @param str
	 * @return
	 */
	public static String encodeMD5(String str) {
		String strDigest = "";
		try {
			// 此 MessageDigest 類爲應用程序提供信息摘要算法的功能,必須用try,catch捕獲
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			byte[] data = md5.digest(str.getBytes("utf-8"));// 轉換爲MD5碼
			strDigest = bytesToHexString(data);
		} catch (Exception ex) {
			throw new RuntimeException(ex);
		}
		return strDigest;
	}
}

SHA25

學Java的對哈希算法都不陌生,畢竟每個類都有hashCode方法。

散列算法(Hash Algorithm),又稱哈希算法,雜湊算法,是一種從任意文件中創造小的數字「指紋」的方法。與指紋一樣,散列算法就是一種以較短的信息來保證文件唯一性的標誌,這種標誌與文件的每一個字節都相關,而且難以找到逆向規律。因此,當原有文件發生改變時,其標誌值也會發生改變,從而告訴文件使用者當前的文件已經不是你所需求的文件。

一個優秀的 hash 算法,將能實現:

正向快速:給定明文和 hash 算法,在有限時間和有限資源內能計算出 hash 值。

逆向困難:給定(若干) hash 值,在有限時間內很難(基本不可能)逆推出明文。

輸入敏感:原始輸入信息修改一點信息,產生的 hash 值看起來應該都有很大不同。

衝突避免:很難找到兩段內容不同的明文,使得它們的 hash 值一致(發生衝突)。即對於任意兩個不同的數據塊,其hash值相同的可能性極小;對於一個給定的數據塊,找到和它hash值相同的數據塊極爲困難。

但在不同的使用場景中,如數據結構和安全領域裏,其中對某一些特點會有所側重。

安全散列算法(英語:Secure Hash Algorithm,縮寫爲SHA)是一個密碼散列函數家族,是FIPS所認證的安全散列算法。能計算出一個數字消息所對應到的,長度固定的字符串(又稱消息摘要)的算法。且若輸入的消息不同,它們對應到不同字符串的機率很高。

SHA家族的五個算法,分別是SHA-1,SHA-224,SHA-256,SHA-384,和SHA-512。主要適用於數字簽名標準(DigitalSignature Standard DSS)裏面定義的數字簽名算法(Digital Signature)算法DSA)。比特幣裏面的就是SHA-256算法。

說簡單一些,就是對一個對象的多個關鍵不重複信息組合起來,通過算法生成一個加密字符串。

引入的加密類和MD5一樣:

import java.security.MessageDigest;

下面是算法的具體內容:

/**
     *  利用java原生的摘要實現SHA256加密
     * @param str 加密後的報文
     * @return
     */
    public static String getSHA256StrJava(String str){
        MessageDigest messageDigest;
        String encodeStr = "";
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(str.getBytes("UTF-8"));
            encodeStr = byte2Hex(messageDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return encodeStr;
    }

    /**
     * 將byte轉爲16進制
     * @param bytes
     * @return
     */
    private static String byte2Hex(byte[] bytes){
        StringBuffer stringBuffer = new StringBuffer();
        String temp = null;
        for (int i=0;i<bytes.length;i++){
            temp = Integer.toHexString(bytes[i] & 0xFF);
            if (temp.length()==1){
                //1得到一位的進行補0操作
                stringBuffer.append("0");
            }
            stringBuffer.append(temp);
        }
        return stringBuffer.toString();
    }

AES

AES是一個對稱密碼,旨在取代DES成爲廣泛使用的標準。
具體代碼如下:

/**
	 * aes加密
	 * @param content
	 * @param password
	 * @return
	 */
	public static String encrypt(String content, String password) {
		try {
			KeyGenerator kgen = KeyGenerator.getInstance("AES");
			SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
			random.setSeed(password.getBytes());
			kgen.init(128, random);
			SecretKey secretKey = kgen.generateKey();
			byte[] enCodeFormat = secretKey.getEncoded();
			SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
			Cipher cipher = Cipher.getInstance("AES");
			byte[] byteContent = content.getBytes("utf-8");
			cipher.init(Cipher.ENCRYPT_MODE, key);
			byte[] result = cipher.doFinal(byteContent);
			String str = Base64.getEncoder().encodeToString(result);
			return str; 
		} catch (NoSuchAlgorithmException e) {
			// e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			// e.printStackTrace();
		} catch (InvalidKeyException e) {
			// e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			// e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			// e.printStackTrace();
		} catch (BadPaddingException e) {
			// e.printStackTrace();
		}
		return null;
	}
    /**
	 * aes解密
	 * @param str
	 * @param password
	 * @return
	 */
	public static String decrypt(String str, String password) {
		try {
			byte[] content = Base64.getDecoder().decode(str);
			KeyGenerator kgen = KeyGenerator.getInstance("AES");
			SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
			secureRandom.setSeed(password.getBytes());
			kgen.init(128, secureRandom);
			SecretKey secretKey = kgen.generateKey();
			byte[] enCodeFormat = secretKey.getEncoded();
			SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
			Cipher cipher = Cipher.getInstance("AES");// 創建密碼器
			cipher.init(Cipher.DECRYPT_MODE, key);// 初始化
			byte[] result = cipher.doFinal(content);
			return new String(result,"UTF-8");
		} catch (NoSuchAlgorithmException e) {
			// e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			// e.printStackTrace();
		} catch (InvalidKeyException e) {
			// e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			// e.printStackTrace();
		} catch (BadPaddingException e) {
			// e.printStackTrace();
		} catch (Exception e) {
			// e.printStackTrace();
		}
		return "";
	}


 

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