信息安全基本概念:java
按照RFC2045的定義,Base64被定義爲:Base64內容傳送編碼被設計用來把任意序列的8位字節描述爲一種不易被人直接識別的形式。(The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.) git
使用:推薦使用 org.apache.commons.codec.binary.Base64算法
Base64編碼說明數據庫
Base64編碼要求把3個8位字節(3*8=24)轉化爲4個6位的字節(4*6=24),以後在6位的前面補兩個0,造成8位一個字節的形式。 若是剩下的字符不足3個字節,則用0填充,輸出字符使用'=',所以編碼後輸出的文本末尾可能會出現1或2個'='。apache
爲了保證所輸出的編碼位可讀字符,Base64制定了一個編碼表,以便進行統一轉換。編碼表的大小爲2^6=64,這也是Base64名稱的由來。編程
碼值 | 字符 | 碼值 | 字符 | 碼值 | 字符 | 碼值 | 字符 | |||
---|---|---|---|---|---|---|---|---|---|---|
0 | A | 16 | Q | 32 | g | 48 | w | |||
1 | B | 17 | R | 33 | h | 49 | x | |||
2 | C | 18 | S | 34 | i | 50 | y | |||
3 | D | 19 | T | 35 | j | 51 | z | |||
4 | E | 20 | U | 36 | k | 52 | 0 | |||
5 | F | 21 | V | 37 | l | 53 | 1 | |||
6 | G | 22 | W | 38 | m | 54 | 2 | |||
7 | H | 23 | X | 39 | n | 55 | 3 | |||
8 | I | 24 | Y | 40 | o | 56 | 4 | |||
9 | J | 25 | Z | 41 | p | 57 | 5 | |||
10 | K | 26 | a | 42 | q | 58 | 6 | |||
11 | L | 27 | b | 43 | r | 59 | 7 | |||
12 | M | 28 | c | 44 | s | 60 | 8 | |||
13 | N | 29 | d | 45 | t | 61 | 9 | |||
14 | O | 30 | e | 46 | u | 62 | + | |||
15 | P | 31 | f | 47 | v | 63 | / |
1
2
3
4
5
6
7
|
import
java.util.Base64;
對於標準的Base64:
加密爲字符串使用Base64.getEncoder().encodeToString();
加密爲字節數組使用Base64.getEncoder().encode();
解密使用Base64.getDecoder().decode();
對於URL安全或MIME的Base64,只需將上述getEncoder()getDecoder()更換爲getUrlEncoder()getUrlDecoder()
或getMimeEncoder()和getMimeDecoder()便可。
|
@Test public void testEncodeBase64() throws Exception { byte[] encodeBase64 = org.apache.commons.codec.binary.Base64 .encodeBase64("進行Base64".getBytes("UTF-8")); System.out.println(new String(encodeBase64));//6L+b6KGMQmFzZTY0 } @Test public void testSDecodeBase64() throws Exception { byte[] decodeBase64 = org.apache.commons.codec.binary.Base64 .decodeBase64("6L+b6KGMQmFzZTY0"); System.out.println(new String(decodeBase64));//進行Base64 }
Base58是用於Bitcoin中使用的一種獨特的編碼方式,主要用於產生Bitcoin的錢包地址。相比Base64,Base58不使用數字"0",字母大寫"O",字母大寫"I",和字母小寫"l",以及"+"和"/"符號。數組
基於ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"共計58個安全
import java.io.UnsupportedEncodingException; import java.math.BigInteger; public class Base58 { public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); private static final int[] INDEXES = new int[128]; static { for (int i = 0; i < INDEXES.length; i++) { INDEXES[i] = -1; } for (int i = 0; i < ALPHABET.length; i++) { INDEXES[ALPHABET[i]] = i; } } /** * Encodes the given bytes in base58. No checksum is appended. */ public static String encode(byte[] input) { if (input.length == 0) { return ""; } input = copyOfRange(input, 0, input.length); // Count leading zeroes. int zeroCount = 0; while (zeroCount < input.length && input[zeroCount] == 0) { ++zeroCount; } // The actual encoding. byte[] temp = new byte[input.length * 2]; int j = temp.length; int startAt = zeroCount; while (startAt < input.length) { byte mod = divmod58(input, startAt); if (input[startAt] == 0) { ++startAt; } temp[--j] = (byte) ALPHABET[mod]; } // Strip extra '1' if there are some after decoding. while (j < temp.length && temp[j] == ALPHABET[0]) { ++j; } // Add as many leading '1' as there were leading zeros. while (--zeroCount >= 0) { temp[--j] = (byte) ALPHABET[0]; } byte[] output = copyOfRange(temp, j, temp.length); try { return new String(output, "US-ASCII"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); // Cannot happen. } } public static byte[] decode(String input) throws IllegalArgumentException { if (input.length() == 0) { return new byte[0]; } byte[] input58 = new byte[input.length()]; // Transform the String to a base58 byte sequence for (int i = 0; i < input.length(); ++i) { char c = input.charAt(i); int digit58 = -1; if (c >= 0 && c < 128) { digit58 = INDEXES[c]; } if (digit58 < 0) { throw new IllegalArgumentException("Illegal character " + c + " at " + i); } input58[i] = (byte) digit58; } // Count leading zeroes int zeroCount = 0; while (zeroCount < input58.length && input58[zeroCount] == 0) { ++zeroCount; } // The encoding byte[] temp = new byte[input.length()]; int j = temp.length; int startAt = zeroCount; while (startAt < input58.length) { byte mod = divmod256(input58, startAt); if (input58[startAt] == 0) { ++startAt; } temp[--j] = mod; } // Do no add extra leading zeroes, move j to first non null byte. while (j < temp.length && temp[j] == 0) { ++j; } return copyOfRange(temp, j - zeroCount, temp.length); } public static BigInteger decodeToBigInteger(String input) throws IllegalArgumentException { return new BigInteger(1, decode(input)); } // // number -> number / 58, returns number % 58 // private static byte divmod58(byte[] number, int startAt) { int remainder = 0; for (int i = startAt; i < number.length; i++) { int digit256 = (int) number[i] & 0xFF; int temp = remainder * 256 + digit256; number[i] = (byte) (temp / 58); remainder = temp % 58; } return (byte) remainder; } // // number -> number / 256, returns number % 256 // private static byte divmod256(byte[] number58, int startAt) { int remainder = 0; for (int i = startAt; i < number58.length; i++) { int digit58 = (int) number58[i] & 0xFF; int temp = remainder * 58 + digit58; number58[i] = (byte) (temp / 256); remainder = temp % 256; } return (byte) remainder; } private static byte[] copyOfRange(byte[] source, int from, int to) { byte[] range = new byte[to - from]; System.arraycopy(source, from, range, 0, range.length); return range; } } 複製代碼
Message Digest Algorithm MD5(中文名爲消息摘要算法第五版)爲計算機安全領域普遍使用的一種散列函數,用以提供消息的完整性保護。服務器
MD5即Message-Digest Algorithm 5(信息-摘要算法5),用於確保信息傳輸完整一致。是計算機普遍使用的雜湊算法之一(又譯摘要算法、哈希算法),主流編程語言廣泛已有MD5實現。將數據(如漢字)運算爲另外一固定長度值,是雜湊算法的基礎原理,MD5的前身有MD二、MD3和MD4。網絡
使用:
public void testMD5() throws Exception { String md5Msg = msgSafeBase("測試MD5","MD5"); System.out.println(md5Msg);// c2dbb895a66c3ca924ccdbea49fa6884 } public String msgSafeBase(String msg, String algorithmName) throws Exception { MessageDigest m = MessageDigest.getInstance(algorithmName); m.update(msg.getBytes("UTF8")); byte s[] = m.digest(); return Hex.encodeHexString(s); }
MD5是經常使用的加密算法,也常常用於校驗信息完整,如文件的完整性。用術語講,MD5是一種消息摘要算法(Message Digest Algorithm)。另外還有一種經常使用的消息摘要算法SHA1。若是想了解這些的話,能夠去百度百科:MD五、SHA一、消息摘要算法。
Java已經實現了MD五、SHA1算法。利用java.security.MessageDigest類就能夠獲取字符串和文件的MD5以及SHA1結果。
特色:
- 不管輸入的消息有多長,計算出來的消息摘要的長度老是固定的。
- 通常地,只要輸入的消息不一樣,對其進行摘要之後產生的摘要消息也必不相同;但相同的輸入必會產生相同的輸出。
- 只能進行正向的信息摘要,而沒法從摘要中恢復出任何的消息,甚至根本就找不到任何與原信息相關的信息(不可逆性)。
- 好的摘要算法,沒有人能從中找到「碰撞」或者說極度難找到,雖然「碰撞」是確定存在的(碰撞即不一樣的內容產生相同的摘要)。
應用:
通常地,把對一個信息的摘要稱爲該消息的指紋或數字簽名。數字簽名是保證信息的完整性和不能否認性的方法。數據的完整性是指信宿接收到的消息必定是信源發送的信息,而中間絕無任何更改;信息的不能否認性是指信源不可否認曾經發送過的信息。其實,經過數字簽名還能實現對信源的身份識別(認證),即肯定「信源」是不是信宿意定的通訊夥伴。 數字簽名應該具備惟一性,即不一樣的消息的簽名是不同的;同時還應具備不可僞造性,即不可能找到另外一個消息,使其簽名與已有的消息的簽名同樣;還應具備不可逆性,即沒法根據簽名還原被簽名的消息的任何信息。這些特徵偏偏都是消息摘要算法的特徵,因此消息摘要算法適合做爲數字簽名算法。
有哪些具體的消息摘要算法?
介紹:
message digest 5,消息摘要算法第五版,以前有md2,md4。MD5消息摘要算法( 英語:MD5 Message-Digest Algorithm),一種被普遍使用的密碼散列函數,能夠產生出一個128位(16字節)的散列值(hash value),用於確保信息傳輸完整一致。MD5由羅納德·李維斯特設計,於1992年公開,用以取代MD4算法。
MD5已經普遍使用在爲文件傳輸提供必定的可靠性方面。例如,服務器預先提供一個MD5校驗和,用戶下載完文件之後,用MD5算法計算下載文件的MD5校驗和,而後經過檢查這兩個校驗和是否一致,就能判斷下載的文件是否出錯。應用:
由於消息摘要算法具備惟一性、不可逆性、不可僞造性,所以普遍用於驗證性加密領域,之因此叫驗證性加密是由於不可逆性決定了沒法還原出明文。
加密:一致性驗證、數字證書、安全訪問認證
安全性:
2009年,馮登國、謝濤二人利用差分攻擊,將MD5的碰撞算法複雜度從王小云的2^42進一步下降到2^21,極端狀況下甚至能夠下降至2^10。僅僅2^21的複雜度意味着即使是在2008年的計算機上,也只要幾秒即可以找到一對碰
1.字符串的MD5(下面的代碼有詳細註釋)
public static String stringMD5(String input) { try { // 拿到一個MD5轉換器(若是想要SHA1參數換成」SHA1」) MessageDigest messageDigest =MessageDigest.getInstance("MD5"); // 輸入的字符串轉換成字節數組 byte[] inputByteArray = input.getBytes(); // inputByteArray是輸入字符串轉換獲得的字節數組 messageDigest.update(inputByteArray); // 轉換並返回結果,也是字節數組,包含16個元素 byte[] resultByteArray = messageDigest.digest(); // 字符數組轉換成字符串返回 return byteArrayToHex(resultByteArray); } catch (NoSuchAlgorithmException e) { return null; } }
//下面這個函數用於將字節數組換成成16進制的字符串
public static String byteArrayToHex(byte[] byteArray) { // 首先初始化一個字符數組,用來存放每一個16進制字符 char[] hexDigits = {'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F' }; // new一個字符數組,這個就是用來組成結果字符串的(解釋一下:一個byte是八位二進制,也就是2位十六進制字符(2的8次方等於16的2次方)) char[] resultCharArray =new char[byteArray.length * 2]; // 遍歷字節數組,經過位運算(位運算效率高),轉換成字符放到字符數組中去 int index = 0; for (byte b : byteArray) { resultCharArray[index++] = hexDigits[b>>> 4 & 0xf]; resultCharArray[index++] = hexDigits[b& 0xf]; } // 字符數組組合成字符串返回 return new String(resultCharArray);
從上面代碼能夠看出,使用MessageDigest對字符串進行MD5算法的步驟是,先將字符串轉換成字節數組,在進行MD5算法,最後返回的也是一個字節數組,要咱們本身轉成32位的字符串。
2.文件MD5
對文件進行MD5也能夠像字符串MD5同樣的,首先要把文件轉成字節數組,後面和字符串MD5徹底同樣。
可是若是是一個特別大的文件,一會兒把一個文件的數組所有讀到內存中,那麼估計內存也吃不消。
對於大文件,可使用DigestInputStream。
public static String fileMD5(String inputFile) throws IOException { // 緩衝區大小(這個能夠抽出一個參數) int bufferSize = 256 * 1024; FileInputStream fileInputStream = null; DigestInputStream digestInputStream = null; try { // 拿到一個MD5轉換器(一樣,這裏能夠換成SHA1) MessageDigest messageDigest =MessageDigest.getInstance("MD5"); // 使用DigestInputStream fileInputStream = new FileInputStream(inputFile); digestInputStream = new DigestInputStream(fileInputStream,messageDigest); // read的過程當中進行MD5處理,直到讀完文件 byte[] buffer =new byte[bufferSize]; while (digestInputStream.read(buffer) > 0); // 獲取最終的MessageDigest messageDigest= digestInputStream.getMessageDigest(); // 拿到結果,也是字節數組,包含16個元素 byte[] resultByteArray = messageDigest.digest(); // 一樣,把字節數組轉換成字符串 return byteArrayToHex(resultByteArray); } catch (NoSuchAlgorithmException e) { return null; } finally { try { digestInputStream.close(); } catch (Exception e) { } try { fileInputStream.close(); } catch (Exception e) { } } }
上面的方法本人親測過大小約4G的文件,得出的MD5值和網上下載的一個MD5小工具獲得的MD5值同樣,說明上面的方式沒有什麼問題。不過取大文件的MD5很慢,4G的文件跑一下要一分鐘(I5處理器 6G內存 64位XP系統 本本)。
附1:我在網上還看到一種給文件MD5的方式
public static String getFileMD5String(File file) throws IOException{ FileInputStream in = new FileInputStream(file); FileChannel ch =in.getChannel(); MappedByteBuffer byteBuffer =ch.map(FileChannel.MapMode.READ_ONLY, 0,file.length()); messagedigest.update(byteBuffer); return byteArrayToHex (messagedigest.digest()); }
我也嘗試過這樣的方式,可是若是文件大於2G,那麼這種方式會出現異常。因此不推薦。
附2:測試文件MD5的main方法
public static void main(String[] args) { long startTime = System.currentTimeMillis(); try { System.out.println(fileMD5("E:/軟件/VS2008ProEdition90DayTrialCHSX1435983.iso")); } catch (IOException e) { e.printStackTrace(); } long endTime = System.currentTimeMillis(); System.out.println((endTime - startTime)/1000); }
參考:http://blog.csdn.net/xiao__gui/article/details/8148203
安全哈希算法(Secure Hash Algorithm)主要適用於數字簽名標準(Digital Signature Standard DSS)裏面定義的數字簽名算法(Digital Signature Algorithm DSA)。對於長度小於2^64位的消息,SHA1會產生一個160位的消息摘要。該算法通過加密專家多年來的發展和改進已日益完善,並被普遍使用。該算法的思想是接收一段明文,而後以一種不可逆的方式將它轉換成一段(一般更小)密文,也能夠簡單的理解爲取一串輸入碼(稱爲預映射或信息),並把它們轉化爲長度較短、位數固定的輸出序列即散列值(也稱爲信息摘要或信息認證代碼)的過程。散列函數值能夠說是對明文的一種「指紋」或是「摘要」因此對散列值的數字簽名就能夠視爲對此明文的數字簽名。
sha1已不推薦使用
使用:
@Test public void testSHA() throws Exception { // SHA-1,SHA-256,SHA-384,和SHA-512 String hashMsg = msgSafeBase("測試SHA", "SHA-1"); System.out.println(hashMsg); // sha1:9bfec0ff7027c76c28fdaa51bd5a619c5e2f69bb } public String msgSafeBase(String msg, String algorithmName) throws Exception { MessageDigest m = MessageDigest.getInstance(algorithmName); m.update(msg.getBytes("UTF8")); byte s[] = m.digest(); return Hex.encodeHexString(s); }
HMAC是密鑰相關的哈希運算消息認證碼,HMAC運算利用哈希算法,以一個密鑰和一個消息爲輸入,生成一個消息摘要做爲輸出。
運算做用
使用:
@Test public void testHashMsgCode() throws Exception { String macKey = initMacKey(); System.out.println(macKey); //vTVhh1xBdDTm9/TZhVsOK0+G/Aw2fkCx0gC6KcM7o2lbCy6DyatcUSe66PTu70E7J0r/hhtodcZBPuLI4/aCgw== String msgCode=hashMsgCode("測試HMAC".getBytes(),macKey); System.out.println(msgCode); //7e4f0f95cfef2c8f5af9799d03798e76 } public static String initMacKey() throws Exception { // HmacMD5,HmacSHA1,HmacSHA256,HmacSHA384,HmacSHA512 KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD5"); SecretKey secretKey = keyGenerator.generateKey(); return new String(Base64.encodeBase64(secretKey.getEncoded())); } public static String hashMsgCode(byte[] data, String key) throws Exception { SecretKey secretKey = new SecretKeySpec(Base64.decodeBase64(key), "HmacMD5"); Mac mac = Mac.getInstance(secretKey.getAlgorithm()); mac.init(secretKey); return new String(Hex.encodeHex(mac.doFinal(data))); }
BC加密包:The Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms. This jar contains JCE provider and lightweight API for the Bouncy Castle Cryptography APIs for JDK 1.5 to JDK 1.8.
RIPEMD算法 原始完整性校驗消息摘要
RIPEMD(RACE Integrity Primitives Evaluation Message Digest,RACE原始完整性校驗消息摘要),是Hans Dobbertin等3人在md4,md5的基礎上,於1996年提出來的。算法共有4個標準12八、160、256和320,其對應輸出長度分別爲16字節、20字節、32字節和40字節。不過,讓人難以至信的是RIPEMD的設計者們根本就沒有真正設計256和320位這2種標準,他們只是在128位和160位的基礎上,修改了初始參數和s-box來達到輸出爲256和320位的目的。因此,256位的強度和128至關,而320位的強度和160位至關。RIPEMD創建在md的基礎之上,因此,其添加數據的方式和md5徹底同樣。
除了MD、SHA和MAC三大主流信息摘要算法以外,還有一些不常見的消息摘要算法。包括RipeMD系列、Tiger、Whirlpool和Gost3411算法。同時,RipeMD算法和MAC算法系列相結合,有產生了HmacRipeMD128和HmacRipeMD160兩種算法。
針對這些算法進行簡單介紹
一、RipeMD算法:針對MD4和MD5算法缺陷分析提出的算法。這些算法主要是針對摘要值得長度進行了區分
二、Tiger算法:號稱最快的Hash算法,專門針對64爲機器作優化了。其消息長度爲192位
三、Whirlpool:被列入iso標準。與AES加密標準使用了相同的轉化技術,極大提升了安全性,被稱爲最安全的摘要算法,長度爲512位
四、Gost3411:信息摘要長度爲256位
這些算法的實現java6都沒提供。這裏BouncyCastle進行了支持。其實這些算法的調用都一個樣,就是換一個調用的名字而已。
2、RipeMD算法和HmacRipeMD算法系列
算法 | 摘要長度 | 備註 |
RipeMD128 | 128 | BouncyCastle實現 |
RipeMD160 | 160 | BouncyCastle實現 |
RipeMD256 | 256 | BouncyCastle實現 |
RipeMD320 | 320 | BouncyCastle實現 |
HmacRipeMD128 | 128 | BouncyCastle實現 |
HmacRipeMD160 | 160 | BouncyCastle實現 |
4、總結
一、HmacRipeMD消息摘要的長度與相應的摘要算法的摘要長度相同:HmacRipeMD128與RipeMD128相對應,消息摘要長度都是32個字符的16進制串。HmacRipeMD160與RipeMD160相對應,消息摘要長度都是40個字符的16進制串。
二、BouncyCastle不只僅提供了HmacRipeMD算法的實現,還提供了HmacTiger算法的實現。實現方式與上邊的代碼清單類似