您好,登錄后才能下訂單哦!
本篇內容介紹了“Java中對稱與非對稱加密算法原理與使用方法是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
密碼學是研究編制密碼和破譯密碼的技術科學。以數學為基礎,在加密和解密、攻擊和防守、矛和盾的對抗過程中交替發展起來。從數學算法的角度看,它包含對稱密碼算法、非對稱密碼算法和雜湊算法。
我們先來看下加密中經常提到的一些概念吧!
明文:明文指的是未被加密過的原始數據。
密文:明文被某種加密算法加密之后,會變成密文,從而確保原始數據的安全。密文也可以被解密,得到原始的明文。
密鑰:密鑰是一種參數,它是在明文轉換為密文或將密文轉換為明文的算法中輸入的參數。密鑰分為對稱密鑰與非對稱密鑰,分別應用在對稱加密和非對稱加密上。
對稱加密又叫做私鑰加密,即信息的發送方和接收方使用同一個密鑰去加密和解密數據。對稱加密的特點是算法公開、加密和解密速度快,適合于對大數據量進行加密。
加密過程如下:明文 + 加密算法 + 私鑰 => 密文
解密過程如下:密文 + 解密算法 + 私鑰 => 明文
對稱加密中用到的密鑰叫做私鑰,私鑰表示個人私有的密鑰,即該密鑰不能被泄露。 其加密過程中的私鑰與解密過程中用到的私鑰是同一個密鑰,這也是稱加密之所以稱之為“對稱”的原因。由于對稱加密的算法是公開的,所以一旦私鑰被泄露,那么密文就很容易被破解,所以對稱加密的缺點是密鑰安全管理困難。
如果你不是很理解,就看這個通俗易懂的例子:
甲對乙說,我這有一把鎖,以后我們互相傳消息,就把消息放盒子里,然后用這個鎖鎖上再傳,這個鎖有兩把一模一樣的鑰匙,咱倆一人一把,說完甲把鑰匙遞給了乙。
非對稱加密也叫做公鑰加密。非對稱加密與對稱加密相比,其安全性更好。對稱加密的通信雙方使用相同的密鑰,如果一方的密鑰遭泄露,那么整個通信就會被破解。而非對稱加密使用一對密鑰,即公鑰和私鑰,且二者成對出現。私鑰被自己保存,不能對外泄露。公鑰指的是公共的密鑰,任何人都可以獲得該密鑰。用公鑰或私鑰中的任何一個進行加密,用另一個進行解密。
被公鑰加密過的密文只能被私鑰解密,過程如下:
明文 + 加密算法 + 公鑰 => 密文, 密文 + 解密算法 + 私鑰 => 明文
由于加密和解密使用了兩個不同的密鑰,這就是非對稱加密“非對稱”的原因。非對稱加密的缺點是加密和解密花費時間長、速度慢,只適合對少量數據進行加密。
如果你不是很理解,就看這個通俗易懂的例子:
甲對乙說,我這里有A型號鎖,對應鑰匙A,我給你一大箱子A鎖,但是鑰匙A不給你,以后你給我發消息就用A鎖鎖在盒子里給我,然后我自己用鑰匙A就能打開看。
乙對甲說,我這里有B型號鎖,對應鑰匙B,我給你一大箱子B鎖,但是鑰匙B不給你,以后你給我發消息就用B鎖鎖在盒子里給我,然后我自己用鑰匙B就能打開看。
加密算法分 對稱加密 和 非對稱加密,其中對稱加密算法的加密與解密 密鑰相同,非對稱加密算法的加密密鑰與解密 密鑰不同,此外,還有一類 不需要密鑰 的 散列算法。
常見的 對稱加密 算法主要有 DES、3DES、AES 等,常見的 非對稱算法 主要有 RSA、ECC 等,散列算法 主要有 SHA-1、MD5 等。
名稱 | 安全性 | 速度 |
---|---|---|
MD5 | 中 | 快 |
SHA-1 | 高 | 慢 |
名稱 | 密鑰名稱 | 運行速度 | 安全性 | 資源消耗 |
---|---|---|---|---|
DES | 56位 | 較快 | 低 | 中 |
3DES | 112位或168位 | 慢 | 中 | 高 |
AES | 128、192、256位 | 快 | 高 | 低 |
名稱 | 成熟度 | 運行速度 | 安全性 | 資源消耗 |
---|---|---|---|---|
RSA | 高 | 中 | 高 | 中 |
ECC | 高 | 慢 | 高 | 高 |
對稱加密 的 密鑰管理比較難,不適合互聯網,一般用于內部系統,安全性只能算中等,但加密速度快好 幾個數量級 (軟件加解密速度至少快 100 倍,每秒可以加解密數 M 比特 數據),適合大數據量的加解密處理。非對稱加密 的 密鑰容易管理,安全性也較高,但是加密速度比較慢,適合 小數據量 加解密或數據簽名。
MD5 用的是 哈希函數,它的典型應用是對一段信息產生 信息摘要,以 防止被篡改。嚴格來說,MD5 不是一種 加密算法 而是 摘要算法。無論是多長的輸入,MD5 都會輸出長度為 128bits 的一個串 (通常用 16 進制 表示為 32 個字符)。
Java使用案例:
public static final byte[] computeMD5(byte[] content) { try { MessageDigest md5 = MessageDigest.getInstance("MD5"); return md5.digest(content); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } }
SHA1 是和 MD5 一樣流行的 消息摘要算法,然而 SHA1 比 MD5 的 安全性更強。對于長度小于 2 ^ 64 位的消息,SHA1 會產生一個 160 位的 消息摘要。基于 MD5、SHA1 的信息摘要特性以及 不可逆 (一般而言),可以被應用在檢查 文件完整性 以及 數字簽名 等場景。
Java使用案例:
public static byte[] computeSHA1(byte[] content) { try { MessageDigest sha1 = MessageDigest.getInstance("SHA1"); return sha1.digest(content); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } }
MAC 是密鑰相關的 哈希運算消息認證碼(Hash-based Message Authentication Code),HMAC 運算利用 哈希算法 (MD5、SHA1 等),以 一個密鑰 和 一個消息 為輸入,生成一個 消息摘要 作為 輸出。HMAC 發送方 和 接收方 都有的 key 進行計算,而沒有這把 key 的第三方,則是 無法計算 出正確的 散列值的,這樣就可以 防止數據被篡改。
Java使用案例:
package net.pocrd.util; import net.pocrd.annotation.NotThreadSafe; import net.pocrd.define.ConstField; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.util.Arrays; @NotThreadSafe public class HMacHelper { private static final Logger logger = LoggerFactory.getLogger(HMacHelper.class); private Mac mac; /** * MAC算法可選以下多種算法 * HmacMD5/HmacSHA1/HmacSHA256/HmacSHA384/HmacSHA512 */ private static final String KEY_MAC = "HmacMD5"; public HMacHelper(String key) { try { SecretKey secretKey = new SecretKeySpec(key.getBytes(ConstField.UTF8), KEY_MAC); mac = Mac.getInstance(secretKey.getAlgorithm()); mac.init(secretKey); } catch (Exception e) { logger.error("create hmac helper failed.", e); } } public byte[] sign(byte[] content) { return mac.doFinal(content); } public boolean verify(byte[] signature, byte[] content) { try { byte[] result = mac.doFinal(content); return Arrays.equals(signature, result); } catch (Exception e) { logger.error("verify sig failed.", e); } return false; } }
注意:HMAC 算法實例在 多線程環境 下是 不安全的。但是需要在 多線程訪問 時,進行同步的輔助類,使用 ThreadLocal 為 每個線程緩存 一個實例可以避免進行鎖操作。
ES、DES、3DES 都是 對稱 的 塊加密算法,加解密 的過程是 可逆的。常用的有 AES128、AES192、AES256 (默認安裝的 JDK 尚不支持 AES256,需要安裝對應的 jce 補丁進行升級 jce1.7,jce1.8)。
DES 加密算法是一種 分組密碼,以 64 位為 分組對數據 加密,它的 密鑰長度 是 56 位,加密解密 用 同一算法。DES 加密算法是對 密鑰 進行保密,而 公開算法,包括加密和解密算法。這樣,只有掌握了和發送方 相同密鑰 的人才能解讀由 DES加密算法加密的密文數據。因此,破譯 DES 加密算法實際上就是 搜索密鑰的編碼。對于 56 位長度的 密鑰 來說,如果用 窮舉法 來進行搜索的話,其運算次數為 2 ^ 56 次。3DES 是基于 DES 的 對稱算法,對 一塊數據 用 三個不同的密鑰 進行 三次加密,強度更高。
AES 加密算法是密碼學中的 高級加密標準,該加密算法采用 對稱分組密碼體制,密鑰長度的最少支持為 128 位、 192 位、256 位,分組長度 128 位,算法應易于各種硬件和軟件實現。AES 本身就是為了取代 DES 的,AES 具有更好的 安全性、效率 和 靈活性。
Java使用案例:
import net.pocrd.annotation.NotThreadSafe; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.SecureRandom; @NotThreadSafe public class AesHelper { private SecretKeySpec keySpec; private IvParameterSpec iv; public AesHelper(byte[] aesKey, byte[] iv) { if (aesKey == null || aesKey.length < 16 || (iv != null && iv.length < 16)) { throw new RuntimeException("錯誤的初始密鑰"); } if (iv == null) { iv = Md5Util.compute(aesKey); } keySpec = new SecretKeySpec(aesKey, "AES"); this.iv = new IvParameterSpec(iv); } public AesHelper(byte[] aesKey) { if (aesKey == null || aesKey.length < 16) { throw new RuntimeException("錯誤的初始密鑰"); } keySpec = new SecretKeySpec(aesKey, "AES"); this.iv = new IvParameterSpec(Md5Util.compute(aesKey)); } public byte[] encrypt(byte[] data) { byte[] result = null; Cipher cipher = null; try { cipher = Cipher.getInstance("AES/CFB/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); result = cipher.doFinal(data); } catch (Exception e) { throw new RuntimeException(e); } return result; } public byte[] decrypt(byte[] secret) { byte[] result = null; Cipher cipher = null; try { cipher = Cipher.getInstance("AES/CFB/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, keySpec, iv); result = cipher.doFinal(secret); } catch (Exception e) { throw new RuntimeException(e); } return result; } public static byte[] randomKey(int size) { byte[] result = null; try { KeyGenerator gen = KeyGenerator.getInstance("AES"); gen.init(size, new SecureRandom()); result = gen.generateKey().getEncoded(); } catch (Exception e) { throw new RuntimeException(e); } return result; } }
RSA 加密算法是目前最有影響力的 公鑰加密算法,并且被普遍認為是目前 最優秀的公鑰方案 之一。RSA 是第一個能同時用于 加密 和 數字簽名 的算法,它能夠抵抗到目前為止已知的 所有密碼攻擊,已被 ISO 推薦為公鑰數據加密標準。
RSA 加密算法 基于一個十分簡單的數論事實:將兩個大 素數 相乘十分容易,但想要對其乘積進行 因式分解 卻極其困難,因此可以將 乘積 公開作為 加密密鑰。
Java使用案例:
import net.pocrd.annotation.NotThreadSafe; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.Cipher; import java.io.ByteArrayOutputStream; import java.security.KeyFactory; import java.security.Security; import java.security.Signature; import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; @NotThreadSafe public class RsaHelper { private static final Logger logger = LoggerFactory.getLogger(RsaHelper.class); private RSAPublicKey publicKey; private RSAPrivateCrtKey privateKey; static { // 使用bouncycastle作為加密算法實現 Security.addProvider(new BouncyCastleProvider()); } public RsaHelper(String publicKey, String privateKey) { this(Base64Util.decode(publicKey), Base64Util.decode(privateKey)); } public RsaHelper(byte[] publicKey, byte[] privateKey) { try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); if (publicKey != null && publicKey.length > 0) { this.publicKey = (RSAPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey)); } if (privateKey != null && privateKey.length > 0) { this.privateKey = (RSAPrivateCrtKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey)); } } catch (Exception e) { throw new RuntimeException(e); } } public RsaHelper(String publicKey) { this(Base64Util.decode(publicKey)); } public RsaHelper(byte[] publicKey) { try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); if (publicKey != null && publicKey.length > 0) { this.publicKey = (RSAPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey)); } } catch (Exception e) { throw new RuntimeException(e); } } public byte[] encrypt(byte[] content) { if (publicKey == null) { throw new RuntimeException("public key is null."); } if (content == null) { return null; } try { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); int size = publicKey.getModulus().bitLength() / 8 - 11; ByteArrayOutputStream baos = new ByteArrayOutputStream((content.length + size - 1) / size * (size + 11)); int left = 0; for (int i = 0; i < content.length; ) { left = content.length - i; if (left > size) { cipher.update(content, i, size); i += size; } else { cipher.update(content, i, left); i += left; } baos.write(cipher.doFinal()); } return baos.toByteArray(); } catch (Exception e) { throw new RuntimeException(e); } } public byte[] decrypt(byte[] secret) { if (privateKey == null) { throw new RuntimeException("private key is null."); } if (secret == null) { return null; } try { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, privateKey); int size = privateKey.getModulus().bitLength() / 8; ByteArrayOutputStream baos = new ByteArrayOutputStream((secret.length + size - 12) / (size - 11) * size); int left = 0; for (int i = 0; i < secret.length; ) { left = secret.length - i; if (left > size) { cipher.update(secret, i, size); i += size; } else { cipher.update(secret, i, left); i += left; } baos.write(cipher.doFinal()); } return baos.toByteArray(); } catch (Exception e) { logger.error("rsa decrypt failed.", e); } return null; } public byte[] sign(byte[] content) { if (privateKey == null) { throw new RuntimeException("private key is null."); } if (content == null) { return null; } try { Signature signature = Signature.getInstance("SHA1WithRSA"); signature.initSign(privateKey); signature.update(content); return signature.sign(); } catch (Exception e) { throw new RuntimeException(e); } } public boolean verify(byte[] sign, byte[] content) { if (publicKey == null) { throw new RuntimeException("public key is null."); } if (sign == null || content == null) { return false; } try { Signature signature = Signature.getInstance("SHA1WithRSA"); signature.initVerify(publicKey); signature.update(content); return signature.verify(sign); } catch (Exception e) { logger.error("rsa verify failed.", e); } return false; } }
ECC 也是一種 非對稱加密算法,主要優勢是在某些情況下,它比其他的方法使用 更小的密鑰,比如 RSA 加密算法,提供 相當的或更高等級 的安全級別。不過一個缺點是 加密和解密操作 的實現比其他機制 時間長 (相比 RSA 算法,該算法對 CPU 消耗嚴重)。
Java使用案例:
import net.pocrd.annotation.NotThreadSafe; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.Cipher; import java.io.ByteArrayOutputStream; import java.security.KeyFactory; import java.security.Security; import java.security.Signature; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; @NotThreadSafe public class EccHelper { private static final Logger logger = LoggerFactory.getLogger(EccHelper.class); private static final int SIZE = 4096; private BCECPublicKey publicKey; private BCECPrivateKey privateKey; static { Security.addProvider(new BouncyCastleProvider()); } public EccHelper(String publicKey, String privateKey) { this(Base64Util.decode(publicKey), Base64Util.decode(privateKey)); } public EccHelper(byte[] publicKey, byte[] privateKey) { try { KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC"); if (publicKey != null && publicKey.length > 0) { this.publicKey = (BCECPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey)); } if (privateKey != null && privateKey.length > 0) { this.privateKey = (BCECPrivateKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey)); } } catch (ClassCastException e) { throw new RuntimeException("", e); } catch (Exception e) { throw new RuntimeException(e); } } public EccHelper(String publicKey) { this(Base64Util.decode(publicKey)); } public EccHelper(byte[] publicKey) { try { KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC"); if (publicKey != null && publicKey.length > 0) { this.publicKey = (BCECPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(publicKey)); } } catch (Exception e) { throw new RuntimeException(e); } } public byte[] encrypt(byte[] content) { if (publicKey == null) { throw new RuntimeException("public key is null."); } try { Cipher cipher = Cipher.getInstance("ECIES", "BC"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); int size = SIZE; ByteArrayOutputStream baos = new ByteArrayOutputStream((content.length + size - 1) / size * (size + 45)); int left = 0; for (int i = 0; i < content.length; ) { left = content.length - i; if (left > size) { cipher.update(content, i, size); i += size; } else { cipher.update(content, i, left); i += left; } baos.write(cipher.doFinal()); } return baos.toByteArray(); } catch (Exception e) { throw new RuntimeException(e); } } public byte[] decrypt(byte[] secret) { if (privateKey == null) { throw new RuntimeException("private key is null."); } try { Cipher cipher = Cipher.getInstance("ECIES", "BC"); cipher.init(Cipher.DECRYPT_MODE, privateKey); int size = SIZE + 45; ByteArrayOutputStream baos = new ByteArrayOutputStream((secret.length + size + 44) / (size + 45) * size); int left = 0; for (int i = 0; i < secret.length; ) { left = secret.length - i; if (left > size) { cipher.update(secret, i, size); i += size; } else { cipher.update(secret, i, left); i += left; } baos.write(cipher.doFinal()); } return baos.toByteArray(); } catch (Exception e) { logger.error("ecc decrypt failed.", e); } return null; } public byte[] sign(byte[] content) { if (privateKey == null) { throw new RuntimeException("private key is null."); } try { Signature signature = Signature.getInstance("SHA1withECDSA", "BC"); signature.initSign(privateKey); signature.update(content); return signature.sign(); } catch (Exception e) { throw new RuntimeException(e); } } public boolean verify(byte[] sign, byte[] content) { if (publicKey == null) { throw new RuntimeException("public key is null."); } try { Signature signature = Signature.getInstance("SHA1withECDSA", "BC"); signature.initVerify(publicKey); signature.update(content); return signature.verify(sign); } catch (Exception e) { logger.error("ecc verify failed.", e); } return false; } }
“Java中對稱與非對稱加密算法原理與使用方法是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。