SHA:安全雜湊演算法簡析 附例項
前言
體能狀態先於精神狀態,習慣先於決心,聚焦先於喜好。
SHA演算法簡介
1.1 概述
SHA (Secure Hash Algorithm,譯作安全雜湊演算法) 是美國國家安全域性 (NSA) 設計,美國國家標準與技術研究院(NIST) 釋出的一系列密碼雜湊函式。正式名稱為 SHA 的家族第一個成員釋出於 1993年。然而人們給它取了一個非正式的名稱 SHA-0 以避免與它的後繼者混淆。兩年之後, SHA-1,第一個 SHA 的後繼者釋出了。 另外還有四種變體,曾經發布以提升輸出的範圍和變更一些細微設計: SHA-224,SHA-256,SHA-384 和 SHA-512 (這些有時候也被稱做 SHA-2)。
SHA家族的五個演算法,分別是SHA-1、SHA-224、SHA-256、SHA-384,和SHA-512,由美國國家安全域性(NSA)所設計,並由美國國家標準與技術研究院(NIST)釋出;是美國的政府標準。後四者有時並稱為SHA-2。SHA-1在許多安全協定中廣為使用,包括TLS和SSL、PGP、SSH、S/MIME和IPsec,曾被視為是MD5(更早之前被廣為使用的雜湊函式)的後繼者。但SHA-1的安全性如今被密碼學家嚴重質疑;雖然至今尚未出現對SHA-2有效的攻擊,它的演算法跟SHA-1基本上仍然相似;因此有些人開始發展其他替代的雜湊演算法。
1.2 SHA演算法原理
SHA-1是一種資料加密演算法,該演算法的思想是接收一段明文,然後以一種不可逆的方式將它轉換成一段(通常更小)密文,也可以簡單的理解為取一串輸入碼(稱為預對映或資訊),並把它們轉化為長度較短、位數固定的輸出序列即雜湊值(也稱為資訊摘要或資訊認證程式碼)的過程。
單向雜湊函式的安全性在於其產生雜湊值的操作過程具有較強的單向性。如果在輸入序列中嵌入密碼,那麼任何人在不知道密碼的情況下都不能產生正確的雜湊值,從而保證了其安全性。SHA將輸入流按照每塊512位(64個位元組)進行分塊,併產生20個位元組的被稱為資訊認證程式碼或資訊摘要的輸出。
該演算法輸入報文的長度不限,產生的輸出是一個160位的報文摘要。輸入是按512 位的分組進行處理的。SHA-1是不可逆的、防衝突,並具有良好的雪崩效應。
通過雜湊演算法可實現數字簽名實現,數字簽名的原理是將要傳送的明文通過一種函式運算(Hash)轉換成報文摘要(不同的明文對應不同的報文摘要),報文摘要加密後與明文一起傳送給接受方,接受方將接受的明文產生新的報文摘要與傳送方的發來報文摘要解密比較,比較結果一致表示明文未被改動,如果不一致表示明文已被篡改。
1.3 SHA演算法應用
SHA演算法主要用於被政府部門和私營業主用來處理敏感的資訊。例如,支付機構,銀行之間的資料傳輸,有的是使用SHA雜湊算計進行加密。
SHA 安全雜湊演算法
安全雜湊演算法(英語:Secure Hash Algorithm,縮寫為SHA)是一個密碼雜湊函式家族.
和MD5類似,安全雜湊演算法可以根據字串生成一定長度的摘要資訊,該摘要資訊不可逆轉,且不同的字串的摘要資訊相同的概率極低。
SHA家族
SHA 有多個演算法,有一些已經過時不再推薦使用,有一些是安全度很高的,但是會降低效能。
SHA1
對於長度小於2^64位的訊息,SHA1會產生一個160位的訊息摘要。
不可以從訊息摘要中復原資訊;兩個不同的訊息不會產生同樣的訊息摘要,(但會有1x10 ^ 48分之一的機率出現相同的訊息摘要,一般使用時忽略)。
可以用於校驗資訊是否被篡改,比如數字證書的簽名
已經不安全了,所以數字簽名證書多用SHA256
SHA256
SHA256 從功能上來說和 SHA1類似,一般也用於資訊摘要,演算法使用的雜湊值長度是256位
用於數字證書的簽名,一般資料的簽名,目前流行的安全雜湊演算法
SHA384、SHA512
內容略,更安全,也更消耗效能,截止2019年8月20日,這兩種演算法都還沒有大範圍推薦使用,市面上推薦的是 SHA256
安全性
目前而言,SHA1 不安全了,SHA256還是安全的
但是從相容性來說,很多系統依舊支援SHA1,但是最新的則會要求是SHA256
Java 中的 SHA
使用 commons-codec
可以使用 Apache 提供的 jar 包 commons-codec
Maven 依賴如下
<dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.10</version> </dependency>
程式碼舉例
結果是位元組陣列,轉化為 16進位制展示
Java
import org.apache.commons.codec.digest.DigestUtils; public class ShaTest { public static void main(String [] args){ String str="123"; String sha1HexStr=DigestUtils.sha1Hex(str.getBytes()); System.out.println("SHA1"+":"+sha1HexStr.length()+";"+sha1HexStr); String sha256HexStr=DigestUtils.sha256Hex(str.getBytes()); System.out.println("SHA256"+":"+sha256HexStr.length()+";"+sha256HexStr); String sha384HexStr=DigestUtils.sha384Hex(str.getBytes()); System.out.println("SHA384"+":"+sha384HexStr.length()+";"+sha384HexStr); String sha512HexStr=DigestUtils.sha512Hex(str.getBytes()); System.out.println("SHA512"+":"+sha512HexStr.length()+";"+sha512HexStr); } }
結果-使用16進位制展示
SHA1:40;40bd001563085fc35165329ea1ff5c5ecbdbbeef
SHA256:64;a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3
SHA384:96;9a0a82f0c0cf31470d7affede3406cc9aa8410671520b727044eda15b4c25532a9b5cd8aaf9cec4919d76255b6bfb00f
SHA512:128;3c9909afec25354d551dae21590bb26e38d53f2173b8d3dc3eee4c047e7ab1c1eb8b85103e3be7ba613b31bb5c9c36214dc9f14a42fd7a2fdb84856bca5c44c2
下面是其他網友的補充
java程式碼實現SHA演算法
package cn.mars.app.txn.wanglian; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class Sha1Util { /** * SHA1簽名 * @param paramStr 要加簽的字串 * @return */ public static String SHA1(String paramStr) { MessageDigest alg; String result = ""; String tmp = ""; try { alg = MessageDigest.getInstance("SHA-1"); alg.update(paramStr.getBytes()); byte[] bts = alg.digest(); for (int i = 0; i < bts.length; i++) { tmp = (Integer.toHexString(bts[i] & 0xFF)); if (tmp.length() == 1) result += "0"; result += tmp; } } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); } return result; } public static void main(String[] args) { String sha1 = SHA1("111"); System.out.println(sha1); } }
經過加密後的字串的個數是固定的:40
package com.enterise.test; public class SHA1 { private final int[] abcde = { 0x67452301,0xefcdab89,0x98badcfe,0x10325476,0xc3d2e1f0 }; // 摘要資料儲存陣列 private int[] digestInt = new int[5]; // 計算過程中的臨時資料儲存陣列 private int[] tmpData = new int[80]; // 測試 public static void main(String[] args) { String param = ""; System.out.println("加密前:" + param); System.out.println("length-->"+param.length()); String digest = new SHA1().getDigestOfString(param.getBytes()); System.out.println("加密後:" + digest); System.out.println("length-->"+digest.length()); } // 計算sha-1摘要 private int process_input_bytes(byte[] bytedata) { // 初試化常量 System.arraycopy(abcde,digestInt,abcde.length); // 格式化輸入位元組陣列,補10及長度資料 byte[] newbyte = byteArrayFormatData(bytedata); // 獲取資料摘要計算的資料單元個數 int MCount = newbyte.length / 64; // 迴圈對每個資料單元進行摘要計算 for (int pos = 0; pos < MCount; pos++) { // 將每個單元的資料轉換成16個整型資料,並儲存到tmpData的前16個數組元素中 for (int j = 0; j < 16; j++) { tmpData[j] = byteArrayToInt(newbyte,(pos * 64) + (j * 4)); } // 摘要計算函式 encrypt(); } return 20; } // 格式化輸入位元組陣列格式 private byte[] byteArrayFormatData(byte[] bytedata) { // 補0數量 int zeros = 0; // 補位後總位數 int size = 0; // 原始資料長度 int n = bytedata.length; // 模64後的剩餘位數 int m = n % 64; // 計算新增0的個數以及新增10後的總長度 if (m < 56) { zeros = 55 - m; size = n - m + 64; } else if (m == 56) { zeros = 63; size = n + 8 + 64; } else { zeros = 63 - m + 56; size = (n + 64) - m + 64; } // 補位後生成的新陣列內容 byte[] newbyte = new byte[size]; // 複製陣列的前面部分 System.arraycopy(bytedata,newbyte,n); // 獲得陣列Append資料元素的位置 int l = n; // 補1操作 newbyte[l++] = (byte) 0x80; // 補0操作 for (int i = 0; i < zeros; i++) { newbyte[l++] = (byte) 0x00; } // 計算資料長度,補資料長度位共8位元組,長整型 long N = (long) n * 8; byte h8 = (byte) (N & 0xFF); byte h7 = (byte) ((N >> 8) & 0xFF); byte h6 = (byte) ((N >> 16) & 0xFF); byte h5 = (byte) ((N >> 24) & 0xFF); byte h4 = (byte) ((N >> 32) & 0xFF); byte h3 = (byte) ((N >> 40) & 0xFF); byte h2 = (byte) ((N >> 48) & 0xFF); byte h1 = (byte) (N >> 56); newbyte[l++] = h1; newbyte[l++] = h2; newbyte[l++] = h3; newbyte[l++] = h4; newbyte[l++] = h5; newbyte[l++] = h6; newbyte[l++] = h7; newbyte[l++] = h8; return newbyte; } private int f1(int x,int y,int z) { return (x & y) | (~x & z); } private int f2(int x,int z) { return x ^ y ^ z; } private int f3(int x,int z) { return (x & y) | (x & z) | (y & z); } private int f4(int x,int y) { return (x << y) | x >>> (32 - y); } // // 單元摘要計算函式 private void encrypt() { for (int i = 16; i <= 79; i++) { tmpData[i] = f4(tmpData[i - 3] ^ tmpData[i - 8] ^ tmpData[i - 14] ^ tmpData[i - 16],1); } int[] tmpabcde = new int[5]; for (int i1 = 0; i1 < tmpabcde.length; i1++) { tmpabcde[i1] = digestInt[i1]; } for (int j = 0; j <= 19; j++) { int tmp = f4(tmpabcde[0],5) + f1(tmpabcde[1],tmpabcde[2],tmpabcde[3]) + tmpabcde[4] + tmpData[j] + 0x5a827999; tmpabcde[4] = tmpabcde[3]; tmpabcde[3] = tmpabcde[2]; tmpabcde[2] = f4(tmpabcde[1],30); tmpabcde[1] = tmpabcde[0]; tmpabcde[0] = tmp; } for (int k = 20; k <= 39; k++) { int tmp = f4(tmpabcde[0],5) + f2(tmpabcde[1],tmpabcde[3]) + tmpabcde[4] + tmpData[k] + 0x6ed9eba1; tmpabcde[4] = tmpabcde[3]; tmpabcde[3] = tmpabcde[2]; tmpabcde[2] = f4(tmpabcde[1],30); tmpabcde[1] = tmpabcde[0]; tmpabcde[0] = tmp; } for (int l = 40; l <= 59; l++) { int tmp = f4(tmpabcde[0],5) + f3(tmpabcde[1],tmpabcde[3]) + tmpabcde[4] + tmpData[l] + 0x8f1bbcdc; tmpabcde[4] = tmpabcde[3]; tmpabcde[3] = tmpabcde[2]; tmpabcde[2] = f4(tmpabcde[1],30); tmpabcde[1] = tmpabcde[0]; tmpabcde[0] = tmp; } for (int m = 60; m <= 79; m++) { int tmp = f4(tmpabcde[0],tmpabcde[3]) + tmpabcde[4] + tmpData[m] + 0xca62c1d6; tmpabcde[4] = tmpabcde[3]; tmpabcde[3] = tmpabcde[2]; tmpabcde[2] = f4(tmpabcde[1],30); tmpabcde[1] = tmpabcde[0]; tmpabcde[0] = tmp; } for (int i2 = 0; i2 < tmpabcde.length; i2++) { digestInt[i2] = digestInt[i2] + tmpabcde[i2]; } for (int n = 0; n < tmpData.length; n++) { tmpData[n] = 0; } } // 4位元組陣列轉換為整數 private int byteArrayToInt(byte[] bytedata,int i) { return ((bytedata[i] & 0xff) << 24) | ((bytedata[i + 1] & 0xff) << 16) | ((bytedata[i + 2] & 0xff) << 8) | (bytedata[i + 3] & 0xff); } // 整數轉換為4位元組陣列 private void intToByteArray(int intValue,byte[] byteData,int i) { byteData[i] = (byte) (intValue >>> 24); byteData[i + 1] = (byte) (intValue >>> 16); byteData[i + 2] = (byte) (intValue >>> 8); byteData[i + 3] = (byte) intValue; } // 將位元組轉換為十六進位制字串 private static String byteToHexString(byte ib) { char[] Digit = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; char[] ob = new char[2]; ob[0] = Digit[(ib >>> 4) & 0X0F]; ob[1] = Digit[ib & 0X0F]; String s = new String(ob); return s; } // 將位元組陣列轉換為十六進位制字串 private static String byteArrayToHexString(byte[] bytearray) { String strDigest = ""; for (int i = 0; i < bytearray.length; i++) { strDigest += byteToHexString(bytearray[i]); } return strDigest; } // 計算sha-1摘要,返回相應的位元組陣列 public byte[] getDigestOfBytes(byte[] byteData) { process_input_bytes(byteData); byte[] digest = new byte[20]; for (int i = 0; i < digestInt.length; i++) { intToByteArray(digestInt[i],digest,i * 4); } return digest; } // 計算sha-1摘要,返回相應的十六進位制字串 public String getDigestOfString(byte[] byteData) { return byteArrayToHexString(getDigestOfBytes(byteData)); } }
到此這篇關於SHA:安全雜湊演算法的文章就介紹到這了,更多相關SHA安全雜湊演算法內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!