抽象

这是涵盖Java加密算法的三部分博客系列的第2部分。 本系列介绍如何实现以下目标:

  1. 使用SHA–512散列
  2. AES–256
  3. RSA–4096

这第二篇文章详细介绍了如何实现单密钥对称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段):

  1. 算法: AES
  2. 模式: GCM
  3. 填充: PKCS5Padding
  4. 密钥大小: 256位
  5. IV大小: 96位

AES–256使用256位密钥, 需要安装Java密码学扩展(JCE)无限强度软件包。 让我们看一个例子。

注意: 256位密钥需要Java密码术扩展(JCE)无限强度软件包。 如果未安装,则最大为128位密钥。

如果尚未安装,请下载并安装Java Cryptography Extension(JCE)无限强度软件包。 需要使用256位密钥。 否则,必须将以下示例更新为使用128位密钥。

清单1是AesTest.java单元测试。 这是以下内容的完整演示:

  1. 生成并存储AES 256位密钥
  2. AES加密
  3. AES解密

清单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部分–单密钥对称加密相关推荐

  1. 对称密钥加密算法 对称轮数_选择Java加密算法第2部分–单密钥对称加密

    对称密钥加密算法 对称轮数 抽象 这是涵盖Java加密算法的三部分博客系列的第2部分. 该系列涵盖如何实现以下功能: 使用SHA–512散列 AES–256 RSA–4096 这第二篇文章详细介绍了如 ...

  2. 非对称加密 公钥私钥_选择Java加密算法第3部分–公钥/私钥非对称加密

    非对称加密 公钥私钥 抽象 这是涵盖Java加密算法的三部分博客系列的第3部分. 该系列涵盖如何实现以下功能: 使用SHA–512散列 使用AES–256的单密钥对称加密 RSA–4096 这第三篇文 ...

  3. 选择Java加密算法第3部分–公钥/私钥非对称加密

    抽象 这是涵盖Java加密算法的三部分博客系列的第3部分. 本系列介绍如何实现以下目标: 使用SHA–512散列 使用AES–256的单密钥对称加密 RSA–4096 这第三篇文章详细介绍了如何实现非 ...

  4. [译] 最佳安全实践:在 Java 和 Android 中使用 AES 进行对称加密

    原文地址:Security Best Practices: Symmetric Encryption with AES in Java and Android 最佳安全实践:在 Java 和 Andr ...

  5. java 哈希算法_选择Java密码算法第1部分-哈希

    java 哈希算法 抽象 这是涵盖Java加密算法的三部分博客系列文章的第1部分. 该系列涵盖如何实现以下功能: 使用SHA–512散列 使用AES–256的单密钥对称加密 使用RSA–4096的公钥 ...

  6. 选择Java密码算法第1部分-散列

    抽象 这是涵盖Java加密算法的三部分博客系列的第1部分. 本系列介绍如何实现以下目标: 使用SHA–512散列 使用AES–256的单密钥对称加密 使用RSA–4096的公钥/私钥非对称加密 这第一 ...

  7. aes默认填充算法 mysql_Go 实现加密算法系列之对称加密

    点击上方蓝色"Go语言中文网"关注我们,领全套Go资料,每天学习 Go 语言 加密算法简介 加密算法就是加密的方法,在密码学中,加密是将明文信息隐藏起来,使之缺少特殊信息时不可读. ...

  8. 对称加密和非对称加密算法工作过程详解

    非对称加密算法: 加密和解密使用不同的密钥.公钥只能用来加密,而私钥只能用来解密.私钥由用户自己拥有.公钥公开配送,只要有需求即可获得. 优点: 算法安全性高,公钥公开,私钥自己保存 缺点: 加密和解 ...

  9. [转]Java加密算法

    如基本的单向加密算法: BASE64 严格地说,属于编码格式,而非加密算法 MD5(Message Digest algorithm 5,信息摘要算法) SHA(Secure Hash Algorit ...

最新文章

  1. java获取当月1号 的时间chuo_java 获取昨天,上个星期一,本月开始时间戳,怎么写呢?...
  2. Use Batch Apex
  3. 让 CefSharp.WinForms 应用程序同时支持32位(x86)和64位(x64)的解决方案
  4. xcode 常用快捷键
  5. 两大流量“黑洞”联姻了:让你在支付宝上刷微博,在微博上用支付宝买单
  6. 【大数据实战】Logstash采集-Kafka-ElasticSearch检索
  7. android开机动画多长时间_Android 开机动画启动过程详解
  8. 《HTTP权威指南》读书笔记---HTTP概述
  9. 关于小米笔记本pro频繁蓝屏问题
  10. 创维E900V21E、融合机DT741、咪咕MGV2000-非高安版-通刷线刷固件
  11. 支付宝内部功能调用APP的said说明(转载)
  12. 第二次作业-时事点评
  13. 服务器硬盘可以换盘位吗,RAID里的硬盘可以互换槽位吗
  14. Java面试题---第四阶段
  15. 海康威视人脸识别智能终端获取设备事件二次开发Java
  16. [元带你学NVMe协议] NVME 2.0 新技术解决了什么痛点? 会带来哪些变革?
  17. 单片机低功耗配置及注意事项
  18. 动态SQL 模糊查询 联表查询
  19. 同时安装wampserver和xampp
  20. 使用w3c生成xml文件

热门文章

  1. Spark入门(十)之Distinct去重
  2. 图解HashMap和HashSet的内部工作机制
  3. 关于链表的简单创建和遍历
  4. 如何给视频中插入视频,字幕,以及去掉前后广告
  5. 给数据表中的字段添加约束
  6. java正则表达式验证密码_最新密码验证正则表达式
  7. python oj 输入_Python写OJ题时输入问题
  8. FAT12中,如何定位大于一个扇区(512B)的文件内容
  9. Redis学习之缓存穿透、缓存击穿和缓存雪崩详解
  10. eclipse开发jsf_在Eclipse上创建JSF / CDI Maven项目