1. 程式人生 > >如何設計一個安全對外的介面?加簽與驗簽了解一下

如何設計一個安全對外的介面?加簽與驗簽了解一下

## 前言 我們在求職面試中,經常會被問到,如何設計一個安全對外的介面呢? 其實可以回答這一點,加簽和驗籤,這將讓你的介面更加有安全。接下來,本文將和大家一起來學習加簽和驗籤。從理論到實戰,加油哦~ - 密碼學相關概念 - 加簽驗籤概念 - 為什麼需要加簽、驗籤 - 加密演算法簡介 - 加簽驗籤相關API - 加簽驗籤程式碼實現 - 公眾號:撿田螺的小男孩 本文已經收錄到個人github,文章有用的話,可以給個star呀: > https://github.com/whx123/JavaHome ## 密碼學相關概念 ### 明文、密文、金鑰、加密、解密 - 明文:指沒有經過加密的資訊/資料。 - 密文:明文被加密演算法加密之後,會變成密文,以確保資料安全。 - 金鑰:是一種引數,它是在明文轉換為密文或將密文轉換為明文的演算法中輸入的引數。金鑰分為對稱金鑰與非對稱金鑰。 - 加密:將明文變成密文的過程。 - 解密:將密文還原為明文的過程。 ### 對稱加密、非對稱加密 - 對稱加密:加密和解密使用相同金鑰的加密演算法。 ![](https://user-gold-cdn.xitu.io/2020/7/13/17348cbfc793e4aa?w=1391&h=374&f=png&s=44478) - 非對稱加密:非對稱加密演算法需要兩個金鑰(公開金鑰和私有金鑰)。公鑰與私鑰是成對存在的,如果用公鑰對資料進行加密,只有對應的私鑰才能解密。 ![](https://user-gold-cdn.xitu.io/2020/7/13/17348cd0767e5e09?w=1355&h=436&f=png&s=46832) ### 什麼是公鑰私鑰? - 公鑰與私鑰是成對存在的金鑰,如果用公鑰對資料進行加密,只有用對應的私鑰才能解密。 - 其實,公鑰就是公開的祕鑰,私鑰就是要你私自儲存好的祕鑰。 - 非對稱加密演算法需要有一對公私鑰~ > 假設你有一個檔案,你用字母a加密,只有字母b才能解密;或者你用b加密,只有a才能解密,那麼a和b就是一對公私鑰。如果金鑰a公開,金鑰b你就要私自儲存好啦,這時候金鑰a就是公鑰,金鑰b就是私鑰。相反,如果b公開,a就要儲存好,這時候呢,祕鑰b就是公鑰,祕鑰a就是私鑰。 ## 加簽驗籤概念 - **加簽**:用Hash函式把原始報文生成報文摘要,然後用私鑰對這個摘要進行加密,就得到這個報文對應的數字簽名。通常來說呢,請求方會把**數字簽名和報文原文**一併傳送給接收方。 ![](https://user-gold-cdn.xitu.io/2020/7/16/17354dd24a9e8b9b?w=888&h=216&f=png&s=22290) - **驗籤**:接收方拿到原始報文和數字簽名後,用**同一個Hash函式**從報文中生成摘要A。另外,用對方提供的公鑰對數字簽名進行解密,得到摘要B,對比A和B是否相同,就可以得知報文有沒有被篡改過。 ![](https://user-gold-cdn.xitu.io/2020/7/16/17354e218f990b9c?w=999&h=552&f=png&s=55740) ## 為什麼需要加簽驗籤 上小節中,加簽和驗籤我們已經知道概念啦,那麼,為什麼需要加簽和驗籤呢?有些朋友可能覺得,我們不是用**公鑰加密,私鑰解密**就好了嘛? 接下來呢,舉個demo吧。 > 假設現在有A公司,要接入C公司的轉賬系統。在一開始呢,C公司把自己的公鑰寄給A公司,自己收藏好私鑰。A公司這邊的商戶,發起轉賬時,A公司先用C公司的公鑰,對請求報文加密,加密報文到達C公司的轉賬系統時,C公司就用自己的私鑰把報文揭開。假設在加密的報文在傳輸過程中,被中間人Actor獲取了,他也鬱悶,因為他沒有私鑰,看著天鵝肉,又吃不了。本來想修改報文,給自己賬號轉一個億的,哈哈。這個實現方式看起來是天衣無縫,穩得一匹的。 ![](https://user-gold-cdn.xitu.io/2020/7/17/17359e10ce248bdc?w=1260&h=662&f=png&s=86660) 但是呢,如果一開始,C公司把公鑰發給公司A的時候,就被中間人Actor獲取到呢,醬紫就出問題了。 > 中間人Actor截取了C的公鑰,他把自己的公鑰發給了A公司,A誤以為這就是C公司的公鑰。A在發起轉賬時,用Actor的公鑰,對請求報文加密,加密報文到在傳輸過程,Actor又截取了,這時候,他用自己的私鑰解密,然後修改了報文(給自己轉一個億),再用C的公鑰加密,發給C公司,C公司收到報文後,繼續用自己的私鑰解密。最後是不是A公司的轉賬賬戶損失了一個億呢~ ![](https://user-gold-cdn.xitu.io/2020/7/17/17359e91e4e1ab8b?w=1465&h=796&f=png&s=145198) C公司是怎麼區分報文是不是來自A呢,還是被中間人修改過呢?為了表明身份和報文真實性,這就需要**加簽驗籤**啦! > A公司把自己的公鑰也傳送給C公司,私鑰自己保留著。在發起轉賬時,先用自己的私鑰對請求報文加簽,於是得到自己的數字簽名。再把數字簽名和請求報文一起傳送給C公司。C公司收到報文後,拿A的公鑰進行驗籤,如果原始報文和數字簽名的摘要內容不一致,那就是報文被篡改啦~ ![](https://user-gold-cdn.xitu.io/2020/7/17/17359f85b7093d8b?w=1416&h=610&f=png&s=89226) 有些朋友可能有疑問,假設A在發自己的公鑰給C公司的時候,也被中間人Actor截取了呢。嗯嗯,我們來模擬一波Actor又截取了公鑰,看看Actor能幹出什麼事情來~哈哈 > 假設Actor擷取到A的公鑰後,隨後也截取了到A發往C的報文。他擷取到報文後,第一件想做的事肯定是修改報文內容。但是如果單單修改原始報文是不可以的,因為發過去C公司肯定驗籤不過啦。但是呢,數字簽名似乎解不開,因為訊息摘要演算法(hash演算法)無法逆向解開的,只起驗證的作用呢.... 所以呢,公鑰與私鑰是用來加密與加密的,**加簽與驗籤是用來證明身份**,以免被篡改的。 ## 常見加密相關演算法簡介 - 訊息摘要演算法 - 對稱加密演算法 - 非對稱加密演算法 - 國密演算法 ### 訊息摘要演算法: - 相同的明文資料經過相同的訊息摘要演算法會得到相同的密文結果值。 - 資料經過訊息摘要演算法處理,得到的摘要結果值,是無法還原為處理前的資料的。 - 資料摘要演算法也被稱為雜湊(Hash)演算法或雜湊演算法。 - 訊息摘要演算法一般用於簽名驗籤。 訊息摘要演算法主要分三類:MD(Message Digest,訊息摘要演算法)、SHA(Secure Hash Algorithm,安全雜湊演算法)和MAC(Message Authentication Code,訊息認證碼演算法)。 ![](https://user-gold-cdn.xitu.io/2020/7/19/1736332419774a2f?w=1477&h=833&f=png&s=90769) #### MD家族演算法 MD(Message Digest,訊息摘要演算法)家族,包括MD2,MD4,MD5。 - MD2,MD4,MD5 計算的結果都是是一個128位(即16位元組)的雜湊值,用於確保資訊傳輸完整一致。 - MD2的演算法較慢但相對安全,MD4速度很快,但安全性下降,MD5則比MD4更安全、速度更快。 - MD5被廣泛應用於資料完整性校驗、資料(訊息)摘要、資料加密等。 - MD5,可以被破解,對於需要高度安全性的資料,專家一般建議改用其他演算法,如SHA-2。2004年,證實MD5演算法無法防止碰撞攻擊,因此不適用於安全性認證,如SSL公開金鑰認證或是數字簽名等用途。 舉個例子,看看如何獲取字串的MD5值吧: ``` public class MD5Test { public static void main(String[] args) throws UnsupportedEncodingException { String s = "123"; byte[] result = getMD5Bytes(s.getBytes()); StringBuilder stringBuilder = new StringBuilder(); for (byte temp : result) { if (temp >= 0 && temp < 16) { stringBuilder.append("0"); } stringBuilder.append(Integer.toHexString(temp & 0xff)); } System.out.println(s + ",MD5加密後:" + stringBuilder.toString()); } private static byte[] getMD5Bytes(byte[] content) { try { MessageDigest md5 = MessageDigest.getInstance("MD5"); return md5.digest(content); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } } ``` 執行結果: ``` 123,MD5加密後:202cb962ac59075b964b07152d234b70 ``` #### ShA家族演算法 SHA(Secure Hash Algorithm,安全雜湊演算法),包括SHA-0、SHA-1、SHA-2(SHA-256,SHA-512,SHA-224,SHA-384等)、SHA-3。它是在MD演算法基礎上實現的,與MD演算法區別在於**摘要長度**,SHA 演算法的摘要**長度更長,安全性更高**。 >- SHA-0釋出之後很快就被NSA撤回,因為含有會降低密碼安全性的錯誤,它是SHA-1的前身。 >- SHA-1在許多安全協議中廣為使用,包括TLS、GnuPG、SSH、S/MIME和IPsec,是MD5的後繼者。 >- SHA-2包括SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。它的演算法跟SHA-1基本上相似,目前還沒有出現明顯弱點。 >- SHA-3是2015年正式釋出,由於對**MD5出現成功的破解**,以及對SHA-0和SHA-1出現理論上破解的方法,SHA-3應運而生。它與之前演算法不同的是,它是可替換的加密雜湊演算法。 SHA-1、SHA-2(SHA-256,SHA-512,SHA-224,SHA-384)等演算法是比較常用的,我們來看看跟MD5的對比吧
演算法型別 摘要長度(bits) 最大輸入訊息長度(bits) 碰撞攻擊(bits) 效能示例(MiB/s)
MD5 128 無限 ≤18(發現碰撞) 335
SHA-1 160 2^64 − 1 <63(發現碰撞) 192
SHA-224 224 2^64 − 1 112 139
SHA-256 256 2^64 − 1 128 139
SHA-384 384 2^128 − 1 192 154
SHA-512 512 2^128 − 1 256 154
#### MAC演算法家族 MAC演算法 MAC(Message Authentication Code,訊息認證碼演算法),是帶金鑰的Hash函式。輸入金鑰和訊息,輸出一個訊息摘要。 它集合了MD和SHA兩大系列訊息摘要演算法。 - MD 系列演算法: HmacMD2、HmacMD4 和 HmacMD5 ; - SHA 系列演算法:HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384 和 HmacSHA512 。 ### 對稱加密演算法 加密和解密使用**相同金鑰**的加密演算法就是對稱加密演算法。常見的對稱加密演算法有AES、3DES、DES、RC5、RC6等。 ![](https://user-gold-cdn.xitu.io/2020/7/18/17362084a7011362?w=1037&h=538&f=png&s=58865) #### DES 資料加密標準(英語:Data Encryption Standard,縮寫為 DES)是一種對稱金鑰加密塊密碼演算法。 DES演算法的入口引數有三個:Key、Data、Mode。 - Key: 7個位元組共56位,是DES演算法的工作金鑰; - Data: 8個位元組64位,是要被加密或被解密的資料; - Mode: 加密或解密。 #### 3DES 三重資料加密演算法(英語:Triple Data Encryption Algorithm,又稱3DES(Triple DES),是一種對稱金鑰加密塊密碼,相當於是對每個資料塊應用三次資料加密標準(DES)演算法。 #### AES AES,高階加密標準(英語:Advanced Encryption Standard),在密碼學中又稱Rijndael加密法,是美國聯邦政府採用的一種區塊加密標準。 - 採用對稱分組密碼體制,金鑰長度為 128 位、 192 位、256 位,分組長度128位 - 相對於DES ,AES具有更好的 安全性、效率 和 靈活性。 ### 非對稱加密演算法 非對稱加密演算法需要兩個金鑰:公鑰和私鑰。公鑰與私鑰是成對存在的,如果用公鑰對資料進行加密,只有用對應的私鑰才能解密。主要的非對稱加密演算法有:RSA、Elgamal、DSA、D-H、ECC。 ![](https://user-gold-cdn.xitu.io/2020/7/18/173622fcdad2dbf7?w=1145&h=580&f=png&s=66889) #### RSA演算法 - RSA加密演算法是一種非對稱加密演算法,廣泛應用於加密和數字簽名 - RSA演算法原理:兩個大素數的乘積進行因式分解卻極其困難,因此可以將乘積公開作為加密金鑰。 - RSA是被研究得最廣泛的公鑰演算法,從提出到現在,經歷了各種攻擊的考驗,普遍認為是目前最優秀的公鑰方案之一。 #### DSA - DSA(Digital Signature Algorithm,數字簽名演算法),也是一種非對稱加密演算法。 - DSA和RSA區別在,DSA僅用於數字簽名,不能用於資料加密解密。其安全性和RSA相當,但其效能要比RSA好。 #### ECC 演算法 - ECC(Elliptic Curves Cryptography,橢圓曲線密碼編碼學),基於橢圓曲線加密。 - Ecc主要優勢是,在某些情況下,它比其他的方法使用更小的金鑰,比如RSA加密演算法,提供相當的或更高等級的安全級別。 - 它的一個缺點是,加密和解密操作的實現比其他機制時間長 (相比RSA演算法,該演算法對CPU 消耗嚴重)。 ### 國密演算法 國密即國家密碼局認定的國產密碼演算法。為了保障商用密碼的安全性,國家商用密碼管理辦公室制定了一系列密碼標準,即SM1,SM2,SM3,SM4等國密演算法。 ![](https://user-gold-cdn.xitu.io/2020/7/18/173626300d45d399?w=1187&h=528&f=png&s=59252) #### SM1 - SM1,為對稱加密演算法,加密強度為128位,基於硬體實現。 - SM1的加密強度和效能,與AES相當。 #### SM2 - SM2主要包括三部分:簽名演算法、金鑰交換演算法、加密演算法 - SM2用於替換RSA加密演算法,基於ECC,效率較低。 #### SM3 - SM3,即國產訊息摘要演算法。 - 適用於商用密碼應用中的數字簽名和驗證,訊息認證碼的生成與驗證以及隨機數的生成。 #### SM4 - SM4是一個分組演算法,用於無線區域網產品。 - 該演算法的分組長度為128位元,金鑰長度為128位元。 - 加密演算法與金鑰擴充套件演算法都採用32輪非線性迭代結構。 - 解密演算法與加密演算法的結構相同,只是輪金鑰的使用順序相反,解密輪金鑰是加密輪金鑰的逆序。 - 它的功能類似國際演算法的DES。 ## 加簽驗籤相關Java的API 這個小節先介紹一下加簽驗籤需要用到的API吧~ ![](https://user-gold-cdn.xitu.io/2020/7/19/17362cd357281448?w=1449&h=774&f=png&s=147886) ### 加簽相關API ``` - java.security.Signature.getInstance(String algorithm); //根據對應演算法,初始化簽名物件 - KeyFactory.getInstance(String algorithm);// 根據對應演算法,生成KeyFactory物件 - KeyFactory.generatePrivate(KeySpec keySpec); //生成私鑰 - java.security.Signature.initSign(PrivateKey privateKey) //由私鑰,初始化加簽物件 - java.security.Signature.update(byte[] data) //把原始報文更新到加簽物件 - java.security.Signature.sign();//加簽 ``` **Signature.getInstance(String algorithm);** - 根據對應演算法,初始化簽名物件 - algorithm引數可以取SHA256WithRSA或者MD5WithRSA等引數,SHA256WithRSA表示生成摘要用的是SHA256演算法,簽名加簽用的是RSA演算法 **KeyFactory.getInstance(String algorithm);** - 根據對應演算法,生成KeyFactory物件,比如你的公私鑰用的是RSA演算法,那麼就傳入RSA **KeyFactory.generatePrivate(KeySpec keySpec)** - 生成私鑰,加簽用的是私鑰哈,所以需要通過KeyFactory先構造一個私鑰物件。 **Signature.initSign(PrivateKey privateKey)** - 加簽用的是私鑰,所以傳入私鑰,初始化加簽物件 **Signature.update(byte[] data)** - 把原始報文更新到加簽物件 **java.security.Signature.sign();** - 進行加簽操作 ### 驗籤相關API ``` - java.security.Signature.getInstance(String algorithm); //根據對應演算法,初始化簽名物件 - KeyFactory.getInstance(String algorithm);// 根據對應演算法,生成KeyFactory物件 - KeyFactory.generatePublic(KeySpec keySpec); //生成公鑰 - java.security.Signature.initVerify(publicKey); //由公鑰,初始化驗籤物件 - java.security.Signature.update(byte[] data) //把原始報文更新到驗籤物件 - java.security.Signature.verify(byte[] signature);//驗籤 ``` **Signature.getInstance(String algorithm)** - 根據對應演算法,初始化簽名物件,注意驗籤和加簽是需要用相同的algorithm演算法引數哦~ **KeyFactory.getInstance(String algorithm);** - 根據對應演算法,生成KeyFactory物件 **KeyFactory.generatePublic(KeySpec keySpec);** - 生成公鑰,驗籤用的是公鑰,通過KeyFactory先構造一個公鑰物件 **Signature.initVerify(publicKey); ** - 公鑰驗籤,所以傳入公鑰物件引數,初始化驗籤物件 **Signature.update(byte[] data)** - 把原始報文更新到加簽物件 **Signature.verify(byte[] signature);** - 進行驗籤操作 ## 加簽驗籤程式碼實現 前幾個小節討論完概念,是時候上程式碼實戰了,如下: ``` package pattern; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.*; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; /** * 加簽驗籤demo * @Author 撿田螺的小男孩 */ public class SignatureTest { //公鑰字串 private static final String PUBLIC_KEY_STR = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDaJzVjC5K6kbS2YE2fiDs6H8pB\n" + "JFDGEYqqJJC9I3E0Ebr5FsofdImV5eWdBSeADwcR9ppNbpORdZmcX6SipogKx9PX\n" + "5aAO4GPesroVeOs91xrLEGt/arteW8iSD+ZaGDUVV3+wcEdci/eCvFlc5PUuZJou\n" + "M2XZaDK4Fg2IRTfDXQIDAQAB"; //私鑰字串 private static final String PRIVATE_KEY_STR = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANonNWMLkrqRtLZg\n" + "TZ+IOzofykEkUMYRiqokkL0jcTQRuvkWyh90iZXl5Z0FJ4APBxH2mk1uk5F1mZxf\n" + "pKKmiArH09floA7gY96yuhV46z3XGssQa39qu15byJIP5loYNRVXf7BwR1yL94K8\n" + "WVzk9S5kmi4zZdloMrgWDYhFN8NdAgMBAAECgYA9bz1Bn0i68b2KfqRdgOfs/nbe\n" + "0XNN1DLQp2t7WDfRCg01iI1zPkZgyFVZWtI85f5/uIrLs5ArLosL1oNuqqc0nNne\n" + "CvJK+ZxvA98Hx3ZqYTzDnleR054YhofL5awbhSciYVic204DOG1rhSsYWMqtX7J7\n" + "3geoWL7TYdMfYXcCAQJBAPMMKsz6ZJh98EeQ1tDG5gpAGWFQkYNrxZDelP/LjeO0\n" + "TP3XkQnIpcaZoCs7V/rRGRGMWwQ2BUdc/01in89ZZ5ECQQDlx2oBc1CtOAm2UAhN\n" + "1xWrPkZWENQ53wTrwXO4qbTGDfBKon0AehLlGCSqxQ71aufLkNO7ZlX0IHTAlnk1\n" + "TvENAkAGSEQ69CXxgx/Y2beTwfBkR2/gghKg0QJUUkyLqBlMz3ZGAXJwTE1sqr/n\n" + "HiuSAiGhwH0ByNuuEotO1sPGukrhAkAMK26a2w+nzPL+u+hkrwKPykGRZ1zGH+Cz\n" + "19AYNKzFXJGgclCqiMydY5T1knBDYUEbj/UW1Mmyn1FvrciHoUG1AkAEMEIuDauz\n" + "JabEAU08YmZw6OoDGsukRWaPfjOEiVhH88p00veM1R37nwhoDMGyEGXVeVzNPvk7\n" + "cELg28MSRzCK"; public static void main(String[] args) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException, IOException, InvalidKeySpecException { //原始報文 String plain = "歡迎大家關注我的公眾號,撿田螺的小男孩"; //加簽 byte[] signatureByte = sign(plain); System.out.println("原始報文是:" + plain); System.out.println("加簽結果:"); System.out.println(new BASE64Encoder().encode(signatureByte)); //驗籤 boolean verifyResult = verify(plain, signatureByte); System.out.println("驗簽結果:" + verifyResult); } /** * 加簽方法 * @param plain * @return * @throws NoSuchAlgorithmException * @throws InvalidKeyException * @throws UnsupportedEncodingException * @throws SignatureException */ private static byte[] sign(String plain) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException, SignatureException { //根據對應演算法,獲取簽名物件例項 Signature signature = Signature.getInstance("SHA256WithRSA"); //獲取私鑰,加簽用的是私鑰,私鑰一般是在配置檔案裡面讀的,這裡為了演示方便,根據私鑰字串生成私鑰物件 PrivateKey privateKey = getPriveteKey(PRIVATE_KEY_STR); //初始化簽名物件 signature.initSign(privateKey); //把原始報文更新到物件 signature.update(plain.getBytes("UTF-8")); //加簽 return signature.sign(); } /** * 驗籤方法 * @param plain * @param signatureByte * @return * @throws NoSuchAlgorithmException * @throws InvalidKeyException * @throws IOException * @throws SignatureException * @throws InvalidKeySpecException */ private static boolean verify(String plain, byte[] signatureByte) throws NoSuchAlgorithmException, InvalidKeyException, IOException, SignatureException, InvalidKeySpecException { //獲取公鑰 PublicKey publicKey = getPublicKey(PUBLIC_KEY_STR); //根據對應演算法,獲取簽名物件例項 Signature signature = Signature.getInstance("SHA256WithRSA"); //初始化簽名物件 signature.initVerify(publicKey); //把原始報文更新到簽名物件 signature.update(plain.getBytes("UTF-8")); //進行驗籤 return signature.verify(signatureByte); } private static PublicKey getPublicKey(String publicKeyStr) throws InvalidKeySpecException, IOException { PublicKey publicKey = null; try { java.security.spec.X509EncodedKeySpec bobPubKeySpec = new java.security.spec.X509EncodedKeySpec( new BASE64Decoder().decodeBuffer(publicKeyStr)); // RSA對稱加密演算法 java.security.KeyFactory keyFactory; keyFactory = java.security.KeyFactory.getInstance("RSA"); // 生成公鑰物件 publicKey = keyFactory.generatePublic(bobPubKeySpec); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return publicKey; } private static PrivateKey getPriveteKey(String privateKeyStr) { PrivateKey privateKey = null; PKCS8EncodedKeySpec priPKCS8; try { priPKCS8 = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(privateKeyStr)); KeyFactory keyf = KeyFactory.getInstance("RSA"); privateKey = keyf.generatePrivate(priPKCS8); } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) { e.printStackTrace(); } return privateKey; } } ``` **執行結果:** ``` 原始報文是:歡迎大家關注我的公眾號,撿田螺的小男孩 加簽結果: Oz15/aybGe42eGHbc+iMoSYHSCc8tfRskTVjjGSTPD4HjadL0CC5JUWNUW0WxHjUb4MvxWo2oeWE Qw0+m61d+JgBMto/TWcVDcgwL/AbObsbWdQ6E/fVRqG13clkE8MyKsjt9Z7tcbwpycYTv0rUR4co rndAVfBdtv5KeV+OXqM= 驗簽結果:true ``` ## 參考與感謝 - [維基百科](https://zh.wikipedia.org/wiki/Wikipedia:%E9%A6%96%E9%A1%B5 "維基百科") - [百度百科](https://baike.baidu.com/ "百度百科") - [常用訊息摘要演算法簡介](https://cloud.tencent.com/developer/article/1584742 "常用訊息摘要演算法簡介") - [淺談常見的七種加密演算法及實現](https://juejin.im/post/5b48b0d7e51d4519962ea383#heading-12 "淺談常見的七種加密演算法及實現") - [【易錯概念】國密演算法SM1(SCB2)、SM2、SM3、SM4、SM7、SM9、ZUC](https://www.jianshu.com/p/8c3657a1769f "【易錯概念】國密演算法SM1(SCB2)、SM2、SM3、SM4、SM7、SM9、ZUC") ## 微信公眾號 ![](https://user-gold-cdn.xitu.io/2020/7/12/1734083c7eacb3f5?w=1655&h=919&f=png&s=243781)