加密模式:https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.htmlhtml
ECB
ECB : Electronic codebook, 電子密碼本. 須要加密的消息按照塊密碼的塊大小被分爲數個塊,並對每一個塊進行獨立加密
java
CBC
CBC : Cipher-block chaining, 密碼塊連接. 每一個明文塊先與前一個密文塊進行異或後,再進行加密。在這種方法中,每一個密文塊都依賴於它前面的全部明文塊git
當須要按塊處理的數據, 數據長度不符合塊處理需求時, 按照必定的方法填充滿塊長的規則
NoPadding算法
不填充.
-在DES加密算法下, 要求原文長度必須是8byte的整數倍
-在AES加密算法下, 要求原文長度必須是16byte的整數倍
PKCS5Paddingapache
數據塊的大小爲8位, 不夠就補足windows
Tipsapi
加密模式和填充模式數組
AES/CBC/NoPadding (128) AES/CBC/PKCS5Padding (128) AES/ECB/NoPadding (128) AES/ECB/PKCS5Padding (128) DES/CBC/NoPadding (56) DES/CBC/PKCS5Padding (56) DES/ECB/NoPadding (56) DES/ECB/PKCS5Padding (56) DESede/CBC/NoPadding (168) DESede/CBC/PKCS5Padding (168) DESede/ECB/NoPadding (168) DESede/ECB/PKCS5Padding (168) RSA/ECB/PKCS1Padding (1024, 2048) RSA/ECB/OAEPWithSHA-1AndMGF1Padding (1024, 2048) RSA/ECB/OAEPWithSHA-256AndMGF1Padding (1024, 2048)
加密模式和填充模式例子tomcat
package com.atguigu.desaes; 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 DesDemo { // DES加密算法,key的大小必須是8個字節 public static void main(String[] args) throws Exception { String input ="硅谷"; // DES加密算法,key的大小必須是8個字節 String key = "12345678"; // 指定獲取Cipher的算法,若是沒有指定加密模式和填充模式,ECB/PKCS5Padding就是默認值 // String transformation = "DES"; // 9PQXVUIhaaQ= //String transformation = "DES/ECB/PKCS5Padding"; // 9PQXVUIhaaQ= // CBC模式,必須指定初始向量,初始向量中密鑰的長度必須是8個字節 //String transformation = "DES/CBC/PKCS5Padding"; // 9PQXVUIhaaQ= // NoPadding模式,原文的長度必須是8個字節的整倍數 ,因此必須把 硅谷改爲硅谷12 String transformation = "DES/CBC/NoPadding"; // 9PQXVUIhaaQ= // 指定獲取密鑰的算法 String algorithm = "DES"; String encryptDES = encryptDES(input, key, transformation, algorithm); System.out.println("加密:" + encryptDES); // String s = dncryptDES(encryptDES, key, transformation, algorithm); // System.out.println("解密:" + s); } /** * 使用DES加密數據 * * @param input : 原文 * @param key : 密鑰(DES,密鑰的長度必須是8個字節) * @param transformation : 獲取Cipher對象的算法 * @param algorithm : 獲取密鑰的算法 * @return : 密文 * @throws Exception */ private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception { // 獲取加密對象 Cipher cipher = Cipher.getInstance(transformation); // 建立加密規則 // 第一個參數key的字節 // 第二個參數表示加密算法 SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm); // ENCRYPT_MODE:加密模式 // DECRYPT_MODE: 解密模式 // 初始向量,參數表示跟誰進行異或,初始向量的長度必須是8位 // IvParameterSpec iv = new IvParameterSpec(key.getBytes()); // 初始化加密模式和算法 cipher.init(Cipher.ENCRYPT_MODE,sks); // 加密 byte[] bytes = cipher.doFinal(input.getBytes()); // 輸出加密後的數據 String encode = Base64.encode(bytes); return encode; } /** * 使用DES解密 * * @param input : 密文 * @param key : 密鑰 * @param transformation : 獲取Cipher對象的算法 * @param algorithm : 獲取密鑰的算法 * @throws Exception * @return: 原文 */ private static String dncryptDES(String input, String key, String transformation, String algorithm) throws Exception { // 1,獲取Cipher對象 Cipher cipher = Cipher.getInstance(transformation); // 指定密鑰規則 SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm); // IvParameterSpec iv = new IvParameterSpec(key.getBytes()); cipher.init(Cipher.DECRYPT_MODE, sks); // 3. 解密 byte[] bytes = cipher.doFinal(Base64.decode(input)); return new String(bytes); } }
運行程序:
安全
不管輸入的消息有多長,計算出來的消息摘要的長度老是固定的。例如應用MD5算法摘要的消息有128個比特位,用SHA-1算法摘要的消息最終有160比特位的輸出
只要輸入的消息不一樣,對其進行摘要之後產生的摘要消息也必不相同;但相同的輸入必會產生相同的輸出
消息摘要是單向、不可逆的
常見算法 :
- MD5 - SHA1 - SHA256 - SHA512
百度搜索 tomcat ,進入官網下載 ,會常常發現有 sha1,sha512 , 這些都是數字摘要
public class DigestDemo1 { public static void main(String[] args) throws Exception{ // 原文 String input = "aa"; // 算法 String algorithm = "MD5"; // 獲取數字摘要對象 MessageDigest messageDigest = MessageDigest.getInstance(algorithm); // 獲取消息數字摘要的字節數組 byte[] digest = messageDigest.digest(input.getBytes()); System.out.println(new String(digest)); } }
運行:
public class DigestDemo1 { public static void main(String[] args) throws Exception{ // 原文 String input = "aa"; // 算法 String algorithm = "MD5"; // 獲取數字摘要對象 MessageDigest messageDigest = MessageDigest.getInstance(algorithm); // 消息數字摘要 byte[] digest = messageDigest.digest(input.getBytes()); // System.out.println(new String(digest)); // base64編碼 System.out.println(Base64.encode(digest)); } }
運行結果:
這裏還須要注意一點,數字摘要通常是十六進制的,因此咱們在base64轉碼格式的基礎上再轉成16進制。
public class DigestDemo1 { public static void main(String[] args) throws Exception{ // 4124bc0a9335c27f086f24ba207a4912 md5 在線校驗 // QSS8CpM1wn8IbyS6IHpJEg== 消息摘要使用的是16進制 // 原文 String input = "aa"; // 算法 String algorithm = "MD5"; // 獲取數字摘要對象 MessageDigest messageDigest = MessageDigest.getInstance(algorithm); // 消息數字摘要 byte[] digest = messageDigest.digest(input.getBytes()); // System.out.println(new String(digest)); // base64編碼 // System.out.println(Base64.encode(digest)); // 建立對象用來拼接 StringBuilder sb = new StringBuilder(); for (byte b : digest) { // 轉成 16進制 String s = Integer.toHexString(b & 0xff); //System.out.println(s); if (s.length() == 1){ // 若是生成的字符只有一個,前面補0 s = "0"+s; } sb.append(s); } System.out.println(sb.toString()); } }
public class DigestDemo1 { public static void main(String[] args) throws Exception{ // 4124bc0a9335c27f086f24ba207a4912 md5 在線校驗 // QSS8CpM1wn8IbyS6IHpJEg== 消息摘要使用的是16進制 // 原文 String input = "aa"; // 算法 String algorithm = "MD5"; // 獲取數字摘要對象 String md5 = getDigest(input, "MD5"); System.out.println(md5); String sha1 = getDigest(input, "SHA-1"); System.out.println(sha1); String sha256 = getDigest(input, "SHA-256"); System.out.println(sha256); String sha512 = getDigest(input, "SHA-512"); System.out.println(sha512); } private static String toHex(byte[] digest) throws Exception { // System.out.println(new String(digest)); // base64編碼 // System.out.println(Base64.encode(digest)); // 建立對象用來拼接 StringBuilder sb = new StringBuilder(); for (byte b : digest) { // 轉成 16進制 String s = Integer.toHexString(b & 0xff); if (s.length() == 1){ // 若是生成的字符只有一個,前面補0 s = "0"+s; } sb.append(s); } System.out.println("16進制數據的長度:" + sb.toString().getBytes().length); return sb.toString(); } private static String getDigest(String input, String algorithm) throws Exception { MessageDigest messageDigest = MessageDigest.getInstance(algorithm); // 消息數字摘要 byte[] digest = messageDigest.digest(input.getBytes()); System.out.println("密文的字節長度:" + digest.length); return toHex(digest); } }
運行結果:
public class DigestDemo { public static void main(String[] args) throws Exception{ String input = "aa"; String algorithm = "MD5"; // sha1 能夠實現秒傳功能 String sha1 = getDigestFile("apache-tomcat-9.0.10-windows-x64.zip", "SHA-1"); System.out.println(sha1); String sha512 = getDigestFile("apache-tomcat-9.0.10-windows-x64.zip", "SHA-512"); System.out.println(sha512); String md5 = getDigest("aa", "MD5"); System.out.println(md5); String md51 = getDigest("aa ", "MD5"); System.out.println(md51); } private static String getDigestFile(String filePath, String algorithm) 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); } // 獲取消息摘要對象 MessageDigest messageDigest = MessageDigest.getInstance(algorithm); // 獲取消息摘要 byte[] digest = messageDigest.digest(baos.toByteArray()); System.out.println("密文的字節長度:"+digest.length); return toHex(digest); } private static String getDigest(String input, String algorithm) throws Exception{ MessageDigest messageDigest = MessageDigest.getInstance(algorithm); byte[] digest = messageDigest.digest(input.getBytes()); System.out.println("密文的字節長度:"+digest.length); return toHex(digest); } private static String toHex(byte[] digest) { // System.out.println(new String(digest)); // 消息摘要進行表示的時候,是用16進制進行表示 StringBuilder sb = new StringBuilder(); for (byte b : digest) { // 轉成16進制 String s = Integer.toHexString(b & 0xff); // 保持數據的完整性,前面不夠的用0補齊 if (s.length()==1){ s="0"+s; } sb.append(s); } System.out.println("16進制數據的長度:"+ sb.toString().getBytes().length); return sb.toString(); } }
運行程序 ,獲取 sha-1 和 sha-512 的值:
查看 tomcat 官網上面 sha-1 和 sha-512 的值:
使用 sha-1 算法,能夠實現秒傳功能,無論我們如何修改文件的名字,最後獲得的值是同樣的
總結:
簡介:
① 非對稱加密算法又稱現代加密算法。
② 非對稱加密是計算機通訊安全的基石,保證了加密數據不會被破解。
③ 與對稱加密算法不一樣,非對稱加密算法須要兩個密鑰:公開密鑰(publickey) 和私有密(privatekey)
④ 公開密鑰和私有密鑰是一對
⑤ 若是用公開密鑰對數據進行加密,只有用對應的私有密鑰才能解密。
⑥ 若是用私有密鑰對數據進行加密,只有用對應的公開密鑰才能解密。
⑦ 由於加密和解密使用的是兩個不一樣的密鑰,因此這種算法叫做非對稱加密算法。
示例:
public class RSAdemo { public static void main(String[] args) throws Exception { // 加密算法 String algorithm = "RSA"; // 建立密鑰對生成器對象 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm); // 生成密鑰對 KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 生成私鑰 PrivateKey privateKey = keyPair.getPrivate(); // 生成公鑰 PublicKey publicKey = keyPair.getPublic(); // 獲取私鑰字節數組 byte[] privateKeyEncoded = privateKey.getEncoded(); // 獲取公鑰字節數組 byte[] publicKeyEncoded = publicKey.getEncoded(); // 對公私鑰進行base64編碼 String privateKeyString = Base64.encode(privateKeyEncoded); String publicKeyString = Base64.encode(publicKeyEncoded); // 打印私鑰 System.out.println(privateKeyString); // 打印公鑰 System.out.println(publicKeyString); } }
運行程序:先打印的是私鑰 , 後面打印的是公鑰。
public class RSAdemo { public static void main(String[] args) throws Exception { String input = "硅谷"; // 加密算法 String algorithm = "RSA"; // 建立密鑰對生成器對象 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm); // 生成密鑰對 KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 生成私鑰 PrivateKey privateKey = keyPair.getPrivate(); // 生成公鑰 PublicKey publicKey = keyPair.getPublic(); // 獲取私鑰字節數組 byte[] privateKeyEncoded = privateKey.getEncoded(); // 獲取公鑰字節數組 byte[] publicKeyEncoded = publicKey.getEncoded(); // 對公私鑰進行base64編碼 String privateKeyString = Base64.encode(privateKeyEncoded); String publicKeyString = Base64.encode(publicKeyEncoded); // 建立加密對象 // 參數表示加密算法 Cipher cipher = Cipher.getInstance(algorithm); // 初始化加密 // 第一個參數:加密的模式 // 第二個參數:使用私鑰進行加密 cipher.init(Cipher.ENCRYPT_MODE,privateKey); // 私鑰加密 byte[] bytes = cipher.doFinal(input.getBytes()); System.out.println(Base64.encode(bytes)); } }
運行結果:
public class RSAdemo { public static void main(String[] args) throws Exception { String input = "硅谷"; // 加密算法 String algorithm = "RSA"; // 建立密鑰對生成器對象 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm); // 生成密鑰對 KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 生成私鑰 PrivateKey privateKey = keyPair.getPrivate(); // 生成公鑰 PublicKey publicKey = keyPair.getPublic(); // 獲取私鑰字節數組 byte[] privateKeyEncoded = privateKey.getEncoded(); // 獲取公鑰字節數組 byte[] publicKeyEncoded = publicKey.getEncoded(); // 對公私鑰進行base64編碼 String privateKeyString = Base64.encode(privateKeyEncoded); String publicKeyString = Base64.encode(publicKeyEncoded); // 建立加密對象 // 參數表示加密算法 Cipher cipher = Cipher.getInstance(algorithm); // 初始化加密 // 第一個參數:加密的模式 // 第二個參數:使用私鑰進行加密 cipher.init(Cipher.ENCRYPT_MODE,privateKey); // 私鑰加密 byte[] bytes = cipher.doFinal(input.getBytes()); System.out.println(Base64.encode(bytes)); // 私鑰進行解密 cipher.init(Cipher.DECRYPT_MODE,publicKey); // 對密文進行解密,不須要使用base64,由於原文不會亂碼 byte[] bytes1 = cipher.doFinal(bytes); System.out.println(new String(bytes1)); } }
運行程序 ,由於私鑰加密,只能公鑰解密:
public class RSAdemo { public static void main(String[] args) throws Exception { String input = "硅谷"; // 加密算法 String algorithm = "RSA"; // 建立密鑰對生成器對象 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm); // 生成密鑰對 KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 生成私鑰 PrivateKey privateKey = keyPair.getPrivate(); // 生成公鑰 PublicKey publicKey = keyPair.getPublic(); // 獲取私鑰字節數組 byte[] privateKeyEncoded = privateKey.getEncoded(); // 獲取公鑰字節數組 byte[] publicKeyEncoded = publicKey.getEncoded(); // 對公私鑰進行base64編碼 String privateKeyString = Base64.encode(privateKeyEncoded); String publicKeyString = Base64.encode(publicKeyEncoded); // 建立加密對象 // 參數表示加密算法 Cipher cipher = Cipher.getInstance(algorithm); // 初始化加密 // 第一個參數:加密的模式 // 第二個參數:使用私鑰進行加密 cipher.init(Cipher.ENCRYPT_MODE,privateKey); // 私鑰加密 byte[] bytes = cipher.doFinal(input.getBytes()); System.out.println(Base64.encode(bytes)); // 私鑰進行解密 cipher.init(Cipher.DECRYPT_MODE,publicKey); // 對密文進行解密,不須要使用base64,由於原文不會亂碼 byte[] bytes1 = cipher.doFinal(bytes); System.out.println(new String(bytes1)); } }
運行程序:
數字簽名(又稱公鑰數字簽名)是隻有信息的發送者才能產生的別人沒法僞造的一段數字串,這段數字串同時也是對信息的發送者發送信息真實性的一個有效證實。它是一種相似寫在紙上的普通的物理簽名,可是使用了公鑰加密領域的技術來實現的,用於鑑別數字信息的方法。一套數字簽名一般定義兩種互補的運算,一個用於簽名,另外一個用於驗證。數字簽名是非對稱密鑰加密技術與數字摘要技術的應用。
相信咱們都寫過信,在寫信的時候落款處老是要留下本身的名字,用來表示寫信的人是誰。咱們籤的這個字就是生活中的簽名。
而數字簽名呢?其實也是一樣的道理,他的含義是:在網絡中傳輸數據時候,給數據添加一個數字簽名,表示是誰發的數據,並且還能證實數據沒有被篡改。
OK,數字簽名的主要做用就是保證了數據的有效性(驗證是誰發的)和完整性(證實信息沒有被篡改)。下面咱們就來好好地看一下他的底層實現原理是什麼樣子的。
爲了理解得清楚,咱們經過案例一步一步來說解。話說張三有倆好哥們A、B。因爲工做緣由,張三和AB寫郵件的時候爲了安全都須要加密。因而張三想到了數字簽名:
整個思路是這個樣子的:
第一步:加密採用非對稱加密,張三有三把鑰匙,兩把公鑰,送給朋友。一把私鑰留給本身。
第二步:A或者B寫郵件給張三:A先用公鑰對郵件加密,而後張三收到郵件以後使用私鑰解密。
第三步:張三寫郵件給A或者B:
(1)張三寫完郵件,先用hash函數生成郵件的摘要,附着在文章上面,這就完成了數字簽名,而後張三再使用私鑰加密。就能夠把郵件發出去了。
(2)A或者是B收到郵件以後,先把數字簽名取下來,而後使用本身的公鑰解密便可。這時候取下來的數字簽名中的摘要若和張三的一致,那就認爲是張三發來的,再對信件自己使用Hash函數,將獲得的結果,與上一步獲得的摘要進行對比。若是二者一致,就證實這封信未被修改過。
上面的流程咱們使用一張圖來演示一下:
首先把公鑰送給朋友A和B:
還有就是最後一個比較麻煩的,張三給A或者B發郵件:
上面提到咱們對簽名進行驗證時,須要用到公鑰。若是公鑰是僞造的,那咱們沒法驗證數字簽名了,也就根本不可能從數字簽名肯定對方的合法性了。這時候證書就閃亮登場了。咱們可能都有考各類證書的經歷,好比說普通話證書,四六級證書等等,可是歸根結底,到任何場合咱們都能拿出咱們的證書來證實本身確實已經考過了普通話,考過了四六級。這裏的證書也是一樣的道理。
若是不理解證書的做用,咱們能夠舉一個例子,好比說咱們的畢業證書,任何公司都會認可。爲何會認可?由於那是國家發得,你們都信任國家。也就是說只要是國家的認證機構,咱們都信任它是合法的。
那麼這個證書是如何生成的呢?咱們再來看一張圖:
此時即便張三的朋友A把公鑰弄錯了,張三也能夠經過這個證書驗證。
import java.security.*; import com.sun.org.apache.xml.internal.security.utils.Base64; 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 { // 獲取簽名對象 Signature signature = Signature.getInstance(algorithm); // 初始化簽名 signature.initSign(privateKey); // 傳入原文 signature.update(input.getBytes()); // 開始簽名 byte[] sign = signature.sign(); // 對簽名數據進行Base64編碼 return Base64.encode(sign); } /** * 校驗簽名 * * @param input : 原文 * @param algorithm : 算法 * @param publicKey : 公鑰 * @param signaturedData : 簽名 * @return : 數據是否被篡改 * @throws Exception */ private static boolean verifySignature(String input, String algorithm, PublicKey publicKey, String signaturedData) throws Exception { // 獲取簽名對象 Signature signature = Signature.getInstance(algorithm); // 初始化簽名 signature.initVerify(publicKey); // 傳入原文 signature.update(input.getBytes()); // 校驗數據 return signature.verify(Base64.decode(signaturedData)); } }