对称密钥加密算法 对称轮数_选择Java加密算法第2部分–单密钥对称加密
對稱密鑰加密算法 對稱輪數
抽象
這是涵蓋Java加密算法的三部分博客系列的第2部分。 該系列涵蓋如何實現以下功能:
這第二篇文章詳細介紹了如何實現單密鑰對稱AES-256加密。 讓我們開始吧。
免責聲明
這篇文章僅供參考。 在使用所提供的任何信息之前,請認真思考。 從中學到東西,但最終自己做出決定,風險自負。
要求
我使用以下主要技術完成了本文的所有工作。 您可能可以使用不同的技術或版本來做相同的事情,但不能保證。
- Java 1.8.0_152_x64
- Java密碼術擴展(JCE)的無限強度
- NetBeans 8.2(內部版本201609300101)
- Maven 3.0.5(與NetBeans捆綁在一起)
下載
訪問我的GitHub頁面以查看我所有的開源項目。 這篇文章的代碼位于項目中: thoth-cryptography
對稱加密
關于
對稱加密算法基于單個密鑰。 此密鑰用于加密和解密。 因此,對稱算法僅應在嚴格控制密鑰的地方使用。
對稱算法通常用于安全環境中的數據加密和解密。 一個很好的例子是確保微服務通信的安全。 如果OAuth-2 / JWT體系結構超出范圍,則API網關可以使用對稱算法的單密鑰來加密令牌。 然后將此令牌傳遞給其他微服務。 其他微服務使用相同的密鑰來解密令牌。 另一個很好的例子是電子郵件中嵌入的超鏈接。 電子郵件中的超鏈接包含一個編碼的令牌,當單擊該超鏈接時,該令牌允許自動登錄請求處理。 此令牌是由對稱算法生成的高度加密的值,因此只能在應用程序服務器上解碼。 當然,任何時候都需要保護任何類型的密碼或憑證,使用對稱算法對它們進行加密,然后可以使用相同的密鑰對字節進行解密。
截止目前的研究似乎表明,以下是最佳和最安全的單密鑰,對稱加密算法(Sheth,2017年,“選擇正確的算法”,第2段):
AES–256使用256位密鑰, 需要安裝Java密碼學擴展(JCE)無限強度軟件包。 讓我們看一個例子。
注意: 256位密鑰需要Java密碼術擴展(JCE)無限強度軟件包。 如果未安裝,則最大為128位密鑰。
例
如果尚未安裝,請下載并安裝Java Cryptography Extension(JCE)無限強度軟件包。 需要使用256位密鑰。 否則,必須將以下示例更新為使用128位密鑰。
清單1是AesTest.java單元測試。 這是以下內容的完整演示:
清單2顯示了AesSecretKeyProducer.java 。 這是一個幫助程序類,負責產生一個新密鑰或從byte[]復制一個現有密鑰。
清單3顯示了ByteArrayWriter.java ,清單4顯示了ByteArrayReader.java 。 這些是負責將byte[]寫入文件的助手類。 由您決定如何存儲密鑰的byte[] ,但需要將其安全地存儲在某個地方(文件,數據庫,git存儲庫等)。
最后,清單5顯示了Aes.java 。 這是一個幫助程序類,負責加密和解密。
清單1 – AesTest.java類
package org.thoth.crypto.symmetric;import java.io.ByteArrayOutputStream; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Optional; import javax.crypto.SecretKey; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.thoth.crypto.io.ByteArrayReader; import org.thoth.crypto.io.ByteArrayWriter;/**** @author Michael Remijan mjremijan@yahoo.com @mjremijan*/ public class AesTest {static Path secretKeyFile;@BeforeClasspublic static void beforeClass() throws Exception {// Store the SecretKey bytes in the ./target diretory. Do// this so it will be ignore by source control. We don't// want this file committed.secretKeyFile= Paths.get("./target/Aes256.key").toAbsolutePath();// Generate a SecretKey for the testSecretKey secretKey= new AesSecretKeyProducer().produce();// Store the byte[] of the SecretKey. This is the// "private key file" you want to keep safe.ByteArrayWriter writer = new ByteArrayWriter(secretKeyFile);writer.write(secretKey.getEncoded());}@Testpublic void encrypt_and_decrypt_using_same_Aes256_instance() {// setupSecretKey secretKey= new AesSecretKeyProducer().produce(new ByteArrayReader(secretKeyFile).read());Aes aes= new Aes(secretKey);String toEncrypt= "encrypt me";// runbyte[] encryptedBytes= aes.encrypt(toEncrypt, Optional.empty());String decrypted= aes.decrypt(encryptedBytes, Optional.empty());// assertAssert.assertEquals(toEncrypt, decrypted);}public void encrypt_and_decrypt_with_aad_using_same_Aes256_instance() {// setupSecretKey secretKey= new AesSecretKeyProducer().produce(new ByteArrayReader(secretKeyFile).read());Aes aes= new Aes(secretKey);String toEncrypt= "encrypt me aad";// runbyte[] encryptedBytes= aes.encrypt(toEncrypt, Optional.of("JUnit AAD"));String decrypted= aes.decrypt(encryptedBytes, Optional.of("JUnit AAD"));// assertAssert.assertEquals(toEncrypt, decrypted);}@Testpublic void encrypt_and_decrypt_using_different_Aes256_instance()throws Exception {// setupSecretKey secretKey= new AesSecretKeyProducer().produce(new ByteArrayReader(secretKeyFile).read());Aes aesForEncrypt= new Aes(secretKey);Aes aesForDecrypt= new Aes(secretKey);String toEncrypt= "encrypt me";// runbyte[] encryptedBytes= aesForEncrypt.encrypt(toEncrypt, Optional.empty());ByteArrayOutputStream baos= new ByteArrayOutputStream();baos.write(encryptedBytes);String decrypted= aesForDecrypt.decrypt(baos.toByteArray(), Optional.empty());// assertAssert.assertEquals(toEncrypt, decrypted);} }清單2 – AesSecretKeyProducer.java類
package org.thoth.crypto.symmetric;import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec;/**** @author Michael Remijan mjremijan@yahoo.com @mjremijan*/ public class AesSecretKeyProducer {/*** Generates a new AES-256 bit {@code SecretKey}.** @return {@code SecretKey}, never null* @throws RuntimeException All exceptions are caught and re-thrown as {@code RuntimeException}*/public SecretKey produce() {KeyGenerator keyGen;try {keyGen = KeyGenerator.getInstance("AES");keyGen.init(256);SecretKey secretKey = keyGen.generateKey();return secretKey;} catch (Exception ex) {throw new RuntimeException(ex);}}/*** Generates an AES-256 bit {@code SecretKey}.** @param encodedByteArray The bytes this method will use to regenerate a previously created {@code SecretKey}** @return {@code SecretKey}, never null* @throws RuntimeException All exceptions are caught and re-thrown as {@code RuntimeException}*/public SecretKey produce(byte [] encodedByteArray) {try {return new SecretKeySpec(encodedByteArray, "AES");} catch (Exception ex) {throw new RuntimeException(ex);}} }清單3 – ByteArrayWriter.java類
package org.thoth.crypto.io;import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path;/**** @author Michael Remijan mjremijan@yahoo.com @mjremijan*/ public class ByteArrayWriter {protected Path outputFile;private void initOutputFile(Path outputFile) {this.outputFile = outputFile;}private void initOutputDirectory() {Path outputDirectory = outputFile.getParent();if (!Files.exists(outputDirectory)) {try {Files.createDirectories(outputDirectory);} catch (IOException e) {throw new RuntimeException(e);}}}public ByteArrayWriter(Path outputFile) {initOutputFile(outputFile);initOutputDirectory();}public void write(byte[] bytesArrayToWrite) {try (OutputStream os= Files.newOutputStream(outputFile);PrintWriter writer= new PrintWriter(os);){for (int i=0; i<bytesArrayToWrite.length; i++) {if (i>0) {writer.println();}writer.print(bytesArrayToWrite[i]);}} catch (IOException ex) {throw new RuntimeException(ex);}} }清單4 – ByteArrayReader.java類
package org.thoth.crypto.io;import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.file.Path; import java.util.Scanner;/**** @author Michael Remijan mjremijan@yahoo.com @mjremijan*/ public class ByteArrayReader {protected Path inputFile;public ByteArrayReader(Path inputFile) {this.inputFile = inputFile;}public byte[] read() {try (Scanner scanner= new Scanner(inputFile);ByteArrayOutputStream baos= new ByteArrayOutputStream();){while (scanner.hasNext()) {baos.write(Byte.parseByte(scanner.nextLine()));}baos.flush();return baos.toByteArray();} catch (IOException ex) {throw new RuntimeException(ex);}} }清單5 – Aes.java類
package org.thoth.crypto.symmetric;import java.security.SecureRandom; import java.util.Optional; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec;/**** @author Michael Remijan mjremijan@yahoo.com @mjremijan*/ public class Aes {// If you don't have the Java Cryptography Extension// (JCE) Unlimited Strength packaged installed, use// a 128 bit KEY_SIZE.public static int KEY_SIZE = 256;public static int IV_SIZE = 12; // 12bytes * 8 = 96bitspublic static int TAG_BIT_SIZE = 128;public static String ALGORITHM_NAME = "AES";public static String MODE_OF_OPERATION = "GCM";public static String PADDING_SCHEME = "PKCS5Padding";protected SecretKey secretKey;protected SecureRandom secureRandom;public Aes(SecretKey secretKey) {this.secretKey = secretKey;this.secureRandom = new SecureRandom();}public byte[] encrypt(String message, Optional<String> aad) {try {// Transformation specifies algortihm, mode of operation and paddingCipher c = Cipher.getInstance(String.format("%s/%s/%s",ALGORITHM_NAME,MODE_OF_OPERATION,PADDING_SCHEME));// Generate IVbyte iv[] = new byte[IV_SIZE];secureRandom.nextBytes(iv); // SecureRandom initialized using self-seeding// Initialize GCM ParametersGCMParameterSpec spec = new GCMParameterSpec(TAG_BIT_SIZE, iv);// Init for encryptionc.init(Cipher.ENCRYPT_MODE, secretKey, spec, secureRandom);// Add AAD tag data if presentaad.ifPresent(t -> {try {c.updateAAD(t.getBytes("UTF-8"));} catch (Exception e) {throw new RuntimeException(e);}});// Add message to encryptc.update(message.getBytes("UTF-8"));// Encryptbyte[] encryptedBytes= c.doFinal();// Concatinate IV and encrypted bytes. The IV is needed later// in order to to decrypt. The IV value does not need to be// kept secret, so it's OK to encode it in the return value//// Create a new byte[] the combined length of IV and encryptedBytesbyte[] ivPlusEncryptedBytes = new byte[iv.length + encryptedBytes.length];// Copy IV bytes into the new arraySystem.arraycopy(iv, 0, ivPlusEncryptedBytes, 0, iv.length);// Copy encryptedBytes into the new arraySystem.arraycopy(encryptedBytes, 0, ivPlusEncryptedBytes, iv.length, encryptedBytes.length);// Returnreturn ivPlusEncryptedBytes;} catch (Exception e) {throw new RuntimeException(e);}}public String decrypt(byte[] ivPlusEncryptedBytes, Optional<String> aad) {try {// Get IVbyte iv[] = new byte[IV_SIZE];System.arraycopy(ivPlusEncryptedBytes, 0, iv, 0, IV_SIZE);// Initialize GCM ParametersGCMParameterSpec spec = new GCMParameterSpec(TAG_BIT_SIZE, iv);// Transformation specifies algortihm, mode of operation and paddingCipher c = Cipher.getInstance(String.format("%s/%s/%s",ALGORITHM_NAME,MODE_OF_OPERATION,PADDING_SCHEME));// Get encrypted bytesbyte [] encryptedBytes = new byte[ivPlusEncryptedBytes.length - IV_SIZE];System.arraycopy(ivPlusEncryptedBytes, IV_SIZE, encryptedBytes, 0, encryptedBytes.length);// Init for decryptionc.init(Cipher.DECRYPT_MODE, secretKey, spec, secureRandom);// Add AAD tag data if presentaad.ifPresent(t -> {try {c.updateAAD(t.getBytes("UTF-8"));} catch (Exception e) {throw new RuntimeException(e);}});// Add message to decryptc.update(encryptedBytes);// Decryptbyte[] decryptedBytes= c.doFinal();// Returnreturn new String(decryptedBytes, "UTF-8");} catch (Exception e) {throw new RuntimeException(e);}} }摘要
加密并不容易。 簡單的示例將為您的應用程序帶來帶有安全漏洞的實現。 如果您需要單密鑰對稱加密算法,請使用具有256位密鑰和96位IV的密碼AES / GCM / PKCS5Padding。
參考資料
- Java密碼術擴展(JCE)無限強度。 (nd)。 檢索自http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html 。
- Sheth,M.(2017年4月18日)。 Java密碼學中的加密和解密。 從https://www.veracode.com/blog/research/encryption-and-decryption-java-cryptography檢索。
- cpast [表示GCM IV是96位,即96/8 = 12字節]。 (2015年6月4日)。 使用AES–256加密時,我可以使用256位IV [Web日志注釋]。 從https://security.stackexchange.com/questions/90848/encrypting-using-aes-256-can-i-use-256-bits-iv檢索
- Bodewes [強烈建議將GCM IV設置為12個字節(12 * 8 = 96),但可以是任意大小。 其他尺寸將需要其他計算],M。(2015年7月7日)。 密文和標簽大小以及在GCM模式下使用AES進行IV傳輸[Web日志注釋]。 從https://crypto.stackexchange.com/questions/26783/ciphertext-and-tag-size-and-iv-transmission-with-aes-in-gcm-mode檢索。
- Figlesquidge。 (2013年10月18日)。 “密碼”和“操作模式”之間有什么區別? [網絡日志評論]。 從https://crypto.stackexchange.com/questions/11132/what-is-the-difference-between-a-cipher-and-a-mode-of-operation檢索。
- Toust,S.(2013年2月4日)。 為什么對稱和非對稱加密之間的建議密鑰大小會有很大差異? 取自https://crypto.stackexchange.com/questions/6236/why-does-the-recommended-key-size-between-symmetric-and-asymmetric-encryption-di 。
- 卡羅寧(I.)(2012年10月5日)。 密鑰,IV和隨機數之間的主要區別是什么? 從https://crypto.stackexchange.com/questions/3965/what-is-the-main-difference-between-a-key-an-iv-and-a-nonce檢索。
- 分組密碼操作模式。 (2017年11月6日)。 維基百科。 取自https://zh.wikipedia.org/wiki/Block_cipher_mode_of_operation#Initialization_vector_.28IV.29
翻譯自: https://www.javacodegeeks.com/2017/12/choosing-java-cryptographic-algorithms-part-2-single-key-symmetric-encryption.html
對稱密鑰加密算法 對稱輪數
總結
以上是生活随笔為你收集整理的对称密钥加密算法 对称轮数_选择Java加密算法第2部分–单密钥对称加密的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信粉色爱心怎么打出来
- 下一篇: java 方法 示例_Java 9示例–