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è)步驟。
-
示例代碼
/** * 使用凱撒加密方式加密數(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)加密方式
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
數(shù)據(jù)塊的大小為8位, 不夠就補(bǔ)足。
-
AES有的同學(xué)沒(méi)問(wèn)題(https://stackoverflow.com/questions/20770072/aes-cbc-pkcs5padding-vs-aes-cbc-pkcs7padding-with-256-key-size-performance-java)
默認(rèn)情況下, 加密模式和填充模式為 : ECB/PKCS5Padding
如果使用CBC模式, 在初始化Cipher對(duì)象時(shí), 需要增加參數(shù), 初始化向量IV : IvParameterSpec iv = new IvParameterSpec(key.getBytes());
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)); } }