对称密钥加密算法 对称轮数_选择Java加密算法第2部分–单密钥对称加密
對(duì)稱密鑰加密算法 對(duì)稱輪數(shù)
抽象
這是涵蓋Java加密算法的三部分博客系列的第2部分。 該系列涵蓋如何實(shí)現(xiàn)以下功能:
這第二篇文章詳細(xì)介紹了如何實(shí)現(xiàn)單密鑰對(duì)稱AES-256加密。 讓我們開(kāi)始吧。
免責(zé)聲明
這篇文章僅供參考。 在使用所提供的任何信息之前,請(qǐng)認(rèn)真思考。 從中學(xué)到東西,但最終自己做出決定,風(fēng)險(xiǎn)自負(fù)。
要求
我使用以下主要技術(shù)完成了本文的所有工作。 您可能可以使用不同的技術(shù)或版本來(lái)做相同的事情,但不能保證。
- Java 1.8.0_152_x64
- Java密碼術(shù)擴(kuò)展(JCE)的無(wú)限強(qiáng)度
- NetBeans 8.2(內(nèi)部版本201609300101)
- Maven 3.0.5(與NetBeans捆綁在一起)
下載
訪問(wèn)我的GitHub頁(yè)面以查看我所有的開(kāi)源項(xiàng)目。 這篇文章的代碼位于項(xiàng)目中: thoth-cryptography
對(duì)稱加密
關(guān)于
對(duì)稱加密算法基于單個(gè)密鑰。 此密鑰用于加密和解密。 因此,對(duì)稱算法僅應(yīng)在嚴(yán)格控制密鑰的地方使用。
對(duì)稱算法通常用于安全環(huán)境中的數(shù)據(jù)加密和解密。 一個(gè)很好的例子是確保微服務(wù)通信的安全。 如果OAuth-2 / JWT體系結(jié)構(gòu)超出范圍,則API網(wǎng)關(guān)可以使用對(duì)稱算法的單密鑰來(lái)加密令牌。 然后將此令牌傳遞給其他微服務(wù)。 其他微服務(wù)使用相同的密鑰來(lái)解密令牌。 另一個(gè)很好的例子是電子郵件中嵌入的超鏈接。 電子郵件中的超鏈接包含一個(gè)編碼的令牌,當(dāng)單擊該超鏈接時(shí),該令牌允許自動(dòng)登錄請(qǐng)求處理。 此令牌是由對(duì)稱算法生成的高度加密的值,因此只能在應(yīng)用程序服務(wù)器上解碼。 當(dāng)然,任何時(shí)候都需要保護(hù)任何類型的密碼或憑證,使用對(duì)稱算法對(duì)它們進(jìn)行加密,然后可以使用相同的密鑰對(duì)字節(jié)進(jìn)行解密。
截止目前的研究似乎表明,以下是最佳和最安全的單密鑰,對(duì)稱加密算法(Sheth,2017年,“選擇正確的算法”,第2段):
AES–256使用256位密鑰, 需要安裝Java密碼學(xué)擴(kuò)展(JCE)無(wú)限強(qiáng)度軟件包。 讓我們看一個(gè)例子。
注意: 256位密鑰需要Java密碼術(shù)擴(kuò)展(JCE)無(wú)限強(qiáng)度軟件包。 如果未安裝,則最大為128位密鑰。
例
如果尚未安裝,請(qǐng)下載并安裝Java Cryptography Extension(JCE)無(wú)限強(qiáng)度軟件包。 需要使用256位密鑰。 否則,必須將以下示例更新為使用128位密鑰。
清單1是AesTest.java單元測(cè)試。 這是以下內(nèi)容的完整演示:
清單2顯示了AesSecretKeyProducer.java 。 這是一個(gè)幫助程序類,負(fù)責(zé)產(chǎn)生一個(gè)新密鑰或從byte[]復(fù)制一個(gè)現(xiàn)有密鑰。
清單3顯示了ByteArrayWriter.java ,清單4顯示了ByteArrayReader.java 。 這些是負(fù)責(zé)將byte[]寫(xiě)入文件的助手類。 由您決定如何存儲(chǔ)密鑰的byte[] ,但需要將其安全地存儲(chǔ)在某個(gè)地方(文件,數(shù)據(jù)庫(kù),git存儲(chǔ)庫(kù)等)。
最后,清單5顯示了Aes.java 。 這是一個(gè)幫助程序類,負(fù)責(zé)加密和解密。
清單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);}} }摘要
加密并不容易。 簡(jiǎn)單的示例將為您的應(yīng)用程序帶來(lái)帶有安全漏洞的實(shí)現(xiàn)。 如果您需要單密鑰對(duì)稱加密算法,請(qǐng)使用具有256位密鑰和96位IV的密碼AES / GCM / PKCS5Padding。
參考資料
- Java密碼術(shù)擴(kuò)展(JCE)無(wú)限強(qiáng)度。 (nd)。 檢索自http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html 。
- Sheth,M.(2017年4月18日)。 Java密碼學(xué)中的加密和解密。 從https://www.veracode.com/blog/research/encryption-and-decryption-java-cryptography檢索。
- cpast [表示GCM IV是96位,即96/8 = 12字節(jié)]。 (2015年6月4日)。 使用AES–256加密時(shí),我可以使用256位IV [Web日志注釋]。 從https://security.stackexchange.com/questions/90848/encrypting-using-aes-256-can-i-use-256-bits-iv檢索
- Bodewes [強(qiáng)烈建議將GCM IV設(shè)置為12個(gè)字節(jié)(12 * 8 = 96),但可以是任意大小。 其他尺寸將需要其他計(jì)算],M。(2015年7月7日)。 密文和標(biāo)簽大小以及在GCM模式下使用AES進(jìn)行IV傳輸[Web日志注釋]。 從https://crypto.stackexchange.com/questions/26783/ciphertext-and-tag-size-and-iv-transmission-with-aes-in-gcm-mode檢索。
- Figlesquidge。 (2013年10月18日)。 “密碼”和“操作模式”之間有什么區(qū)別? [網(wǎng)絡(luò)日志評(píng)論]。 從https://crypto.stackexchange.com/questions/11132/what-is-the-difference-between-a-cipher-and-a-mode-of-operation檢索。
- Toust,S.(2013年2月4日)。 為什么對(duì)稱和非對(duì)稱加密之間的建議密鑰大小會(huì)有很大差異? 取自https://crypto.stackexchange.com/questions/6236/why-does-the-recommended-key-size-between-symmetric-and-asymmetric-encryption-di 。
- 卡羅寧(I.)(2012年10月5日)。 密鑰,IV和隨機(jī)數(shù)之間的主要區(qū)別是什么? 從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
對(duì)稱密鑰加密算法 對(duì)稱輪數(shù)
總結(jié)
以上是生活随笔為你收集整理的对称密钥加密算法 对称轮数_选择Java加密算法第2部分–单密钥对称加密的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 微信粉色爱心怎么打出来
- 下一篇: java 方法 示例_Java 9示例–