安全散列算法SHA (Secure Hash Algorithm)是美國國家標準和技術局發佈的國家標準FIPS PUB 180-1,通常稱爲SHA-1。其對長度不超過264二進制位的消息產生160位的消息摘要輸出,按512比特塊處理其輸入。java
SHA是一種數據加密算法,該算法通過加密專家多年來的發展和改進已日益完善,如今已成爲公認的最安全的散列算法之一,並被普遍使用。git
該算法的思想是接收一段明文,而後以一種不可逆的方式將它轉換成一段(一般更小)密文,也能夠簡單的理解爲取一串輸入碼(稱爲預映射或信息),並把它們轉化爲長度較短、位數固定的輸出序列即散列值(也稱爲信息摘要或信息認證代碼)的過程。散列函數值能夠說時對明文的一種「指紋」或是「摘要」因此對散列值的數字簽名就能夠視爲對此明文的數字簽名。算法
消息摘要(Message Digest)又稱爲數字摘要(Digital Digest)。它是一個惟一對應一個消息或文本的固定長度的值,它由一個單向Hash加密函數對消息進行做用而產生。若是消息在途中改變了,則接收者經過對收到消息的新產生的摘要與原摘要比較,就可知道消息是否被改變了。所以消息摘要保證了消息的完整性。數組
消息摘要採用單向Hash函數將需加密的明文"摘要"成一串128bit的密文,這一串密文亦稱爲數字指紋(Finger Print),它有固定的長度,且不一樣的明文摘要成密文,其結果老是不一樣的,而一樣的明文其摘要一定一致。這樣這串摘要即可成爲驗證實文是不是"真身"的"指紋"了。安全
摘要:GOST3411
,Keccak
,MD2
,MD4
,MD5
,RIPEMD128
,RIPEMD160
,RIPEMD256
,RIPEMD320
,SHA-1
,SHA-224
,SHA-256
,SHA-384
,SHA-512
,SHA3
,Tiger
和Whirlpool
。app
公鑰和私鑰就是俗稱的不對稱加密方式,是從之前的對稱加密(使用用戶名與密碼)方式的提升。ide
下面用電子郵件的方式說明一下原理:
使用公鑰與私鑰的目的就是實現安全的電子郵件,必須實現以下目的:函數
AES
, Blowfish
, Camellia
, CAST5
, CAST6
,ChaCha
, DES
, DESede
, GOST28147
, HC-128
, HC-256
, IDEA
, ISAAC
, Noekeon
, RC2
, RC4
, RC5-32
, RC5-64
, RC6
, Rijndael
, Salsa20
, SEED
, Serpent
, Skipjack
, TEA/XTEA
, Threefish
, Tnepres
, Twofish
, VMPC
and XSalsa20
.CBC
,CFB
,CTS
,GOFB
,OFB
,OpenPGPCFB
和SIC(或CTR)
。ISO10126d2
, ISO7816d4
, PKCS-5/7
, TBC
, X.923
, and Zero Byte
.ElGamal
, DSA
, ECDSA
, NaccacheStern
and RSA (with blinding)
.ISO9796d1
, OAEP
, and PKCS-1
.注: 上述需求對應於防火牆、加密、數字簽名、身份認證等技術,但其關鍵在於數字簽名技術。post
數字簽名是經過一個單向函數對要傳送的報文進行處理獲得的用以認證報文來源並覈實報文是否發生變化的一個字母數字串。測試
實現數字簽名有不少方法,目前數字簽名採用較多的是公鑰加密技術,如基於RSA Date Security 公司的PKCS( Public Key Cryptography Standards )、Digital Signature Algorithm、x.50九、PGP(Pretty Good Privacy)。
1994年美國標準與技術協會公佈了數字簽名標準(DSS)而使公鑰加密技術普遍應用。公鑰加密系統採用的是非對稱加密算法。
MD2withRSA
, MD4withRSA
,MD5withRSA
, RIPEMD128withRSA
, RIPEMD160withECDSA
, RIPEMD160withRSA
, RIPEMD256withRSA
, SHA-1withRSA
, SHA-224withRSA
, SHA-256withRSAandMGF1
, SHA-384withRSAandMGF1
, SHA-512withRSAandMGF1
, SHA-1withDSA
, and SHA-1withECDSA
對於長度小於2^64位的消息,SHA1會產生一個160位(40個字符)的消息摘要。當接收到消息的時候,這個消息摘要能夠用來驗證數據的完整性。在傳輸的過程當中,數據極可能會發生變化,那麼這時候就會產生不一樣的消息摘要。
代碼以下:
import javax.crypto.Cipher; import java.io.*; import java.security.*; import java.util.Base64; /** * @author: mmzsit * @date: 2018年10月24日 * @Description: * 博客地址:https://blog.mmzsblog.cn * @version V1.0 */ public class EncryptUtil { public static void main(String[] args) { ObjectInputStream inputStream = null; //參數字符串 String userName="測試test0->1"; String orderId="測試ID123456"; String price="666"; //構建用於簽名和傳輸的字符串 StringBuffer bufferStr =new StringBuffer(); bufferStr.append("userName=").append(userName) .append("&orderId=").append(orderId) .append("&price=").append(price); //將構建的字符串轉化爲String類型 String localStr =bufferStr.toString(); //簽名算法加密 try { //隨機生成祕鑰對 // 檢查是否存在這對密鑰,不然生成這些密鑰 if (!areKeysPresent()) { // 使用RSA算法生成一對密鑰,並存儲在指定路徑的指定文件中 generateKey(); } //服務端數字簽名開始 //第一步:用SHA-1算出原文的摘要 byte[] shaDigest = shaEncrypt(localStr); System.out.println("原文本內容:\n"+localStr); String shaStr = new String(shaDigest,"UTF-8"); System.out.println("原文本內容SHA-1算法後:\n"+shaStr); //第二步:使用私鑰對原文進行加密 //讀取文件中的私鑰 inputStream = new ObjectInputStream(new FileInputStream(PRIVATE_KEY_FILE)); final PrivateKey privateKey = (PrivateKey) inputStream.readObject(); //使用私鑰加密 byte[] rsaBytes = rsaEncrypt(shaDigest,privateKey); //第三步:對密文進行BASE64編碼 byte[] base64Str = Base64.getEncoder().encode(rsaBytes); String base64enCode=new String(base64Str,"UTF-8"); System.out.println("加密後的內容:\n"+base64enCode); //簽名加密完成數據傳輸到客戶端 //客戶端驗證簽名開始 //獲取原文 String receiveStr=localStr; //第一步:使用Base64解碼密文 byte[] bese64Decoded =Base64.getDecoder().decode(base64enCode.getBytes("UTF-8")); //第二步:使用公鑰對密文進行解碼 //讀取文件中的公鑰 inputStream = new ObjectInputStream(new FileInputStream(PUBLIC_KEY_FILE)); final PublicKey publicKey = (PublicKey) inputStream.readObject(); //使用公鑰解密 byte[] rsaDecode = rsaDecrypt(bese64Decoded,publicKey); //公鑰解密後的結果 String base64denCode=new String(rsaDecode,"utf-8"); System.out.println("公鑰解密後的結果:\n"+base64denCode); //第三步:驗籤 //讀取解密後的摘要 String sha1=Base64.getEncoder().encodeToString(rsaDecode); //使用Sha-1對原文計算摘要 MessageDigest md =MessageDigest.getInstance("SHA-1"); String sha2=Base64.getEncoder().encodeToString(md.digest(receiveStr.getBytes("utf-8"))); //用Sha-1對原文計算摘要結果和解密後的明文比對 if(sha1.equals(sha2)) { System.out.println("驗籤成功"); } else { System.out.println("驗籤失敗"); } System.out.println("字符串sha1:\n"+sha1); System.out.println("字符串sha2:\n"+sha2); }catch (Exception e) { e.printStackTrace(); } } /** * 用於保存加密算法名稱的字符串 */ public static final String ALGORITHM = "RSA"; /** * * 用於保存加密填充名稱的字符串 * 若是不填寫,那麼RSA/NONE/NoPadding就是Bouncy Castle 的默認 RSA 實現 * 備用: */ public static final String PADDING = "RSA/ECB/PKCS1Padding"; /** * 用於保存安全提供程序名稱的字符串 */ // public static final String PROVIDER = "BC"; /** * 用於保存私鑰文件名稱的字符串 */ public static final String PRIVATE_KEY_FILE = "d:/Temp/private.key"; /** * 用於保存公鑰文件名稱的字符串 */ public static final String PUBLIC_KEY_FILE = "d:/Temp/public.key"; /** * 假設最高安全性(即4096位RSA密鑰或更大)是很是安全 * 使用1024字節生成包含一對私鑰和公鑰的密鑰。 * 將該組密鑰存儲在Prvate.key和Public.key文件中。 * * @throws NoSuchAlgorithmException * @throws IOException * @throws FileNotFoundException */ public static void generateKey() { try { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM); //final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM, PROVIDER); //密鑰位數 keyGen.initialize(1024); //密鑰對 final KeyPair key = keyGen.generateKeyPair(); File privateKeyFile = new File(PRIVATE_KEY_FILE); File publicKeyFile = new File(PUBLIC_KEY_FILE); // 建立文件夾存儲私鑰 if (privateKeyFile.getParentFile() != null) { privateKeyFile.getParentFile().mkdirs(); } privateKeyFile.createNewFile(); // 建立文件夾存儲公鑰 if (publicKeyFile.getParentFile() != null) { publicKeyFile.getParentFile().mkdirs(); } publicKeyFile.createNewFile(); // 建立文件夾保存公鑰 ObjectOutputStream publicKeyOS = new ObjectOutputStream( new FileOutputStream(publicKeyFile)); publicKeyOS.writeObject(key.getPublic()); publicKeyOS.close(); // 建立文件夾保存私鑰 ObjectOutputStream privateKeyOS = new ObjectOutputStream( new FileOutputStream(privateKeyFile)); privateKeyOS.writeObject(key.getPrivate()); privateKeyOS.close(); } catch (Exception e) { e.printStackTrace(); } } /** * 檢查是否已生成一對公鑰和私鑰 * * @return boolean 返回是否生成祕鑰對的標識 */ public static boolean areKeysPresent() { File privateKey = new File(PRIVATE_KEY_FILE); File publicKey = new File(PUBLIC_KEY_FILE); if (privateKey.exists() && publicKey.exists()) { return true; } return false; } /** * 使用公鑰解密數據 * * @param text 待解密文本 * @param key 公鑰 * @return 解密文本 * @throws java.lang.Exception */ public static byte[] rsaDecrypt(byte[] text, PublicKey key) { byte[] cipherText = null; try { // 獲取RSA密碼對象並打印提供程序 // Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); final Cipher cipher = Cipher.getInstance(PADDING); //final Cipher cipher = Cipher.getInstance(PADDING, PROVIDER); // 使用公鑰,ENCRYPT_MODE表示爲解密模式 cipher.init(Cipher.DECRYPT_MODE, key); cipherText = cipher.doFinal(text); } catch (Exception e) { e.printStackTrace(); } return cipherText; } /** * 使用私鑰加密數據 * * @param text 待加密文本 * @param key 私鑰 * @return 加密後的數據 * @throws java.lang.Exception */ public static byte[] rsaEncrypt(byte[] text, PrivateKey key) { byte[] dectyptedText = null; try { // //獲取RSA密碼對象並打印提供程序 // Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); final Cipher cipher = Cipher.getInstance(PADDING); //final Cipher cipher = Cipher.getInstance(PADDING, PROVIDER); // 使用私鑰加密文本 cipher.init(Cipher.ENCRYPT_MODE, key); dectyptedText = cipher.doFinal(text); } catch (Exception ex) { ex.printStackTrace(); } return dectyptedText; } /** * 使用sha-1對摘要進行加密 * @param text 簽名的原始文本 */ public static byte[] shaEncrypt(String text) { //建立消息摘要算法的類 MessageDigest md = null; //因爲接收加密後的摘要的字節數組 byte[] shaDigest = null; try { //使用getInstance("算法")來得到消息摘要 md = MessageDigest.getInstance("SHA-1"); //將摘要轉化爲UTF-8格式的字節數組 byte[] plainText = text.getBytes("UTF-8"); //使用指定的 byte 數組更新摘要 md.update(plainText); //得出SHA-1算法加密後的結果 shaDigest=md.digest(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return shaDigest; } }