用Java进行加密
首先最近,我觉得使用AES256/GCM应该没问题,下面是使用方法:
输入一个字节序列,获取加密后的字节序列,然后解密并获取字节序列。
如果在Java中使用Cipher,GCM标签似乎包含在密文中。因此,在使用其他库(如.NET)进行解密时,似乎需要单独提取标签。
暗号键可以用中文母语进行改述:密码键。如果选择AES256,请确保密钥长度为256位。
如果要输入密码进行解密,则可以将密码的SHA256用作密钥。也可以自动生成密码。
如果要输入密码进行解密,则可以将密码的SHA256用作密钥。也可以自动生成密码。
如果从字符串中获取SHA256并用作密钥,可以像这样操作。(由于例外情况需要适当处理,请在使用时进行正确的处理)
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
.
.
.
public SecretKey getKeyFromPassword(String pass)
{
try {
MessageDigest sha = MessageDigest.getInstance("SHA-256");
sha.update(pass.getBytes(StandardCharsets.UTF_8));
byte[] shakey = sha.digest();
SecretKey ret = new SecretKeySpec(shakey, "AES");
return ret;
}
catch (Exception e) {
Log.warn(e.toString());
return null;
}
}
如果是自动生成,就是这样的。由于需要共享生成的密钥才能进行解密,所以可以通过SecretKey.getEncoded()方法获取字节数组。
用字节数组生成密钥的方法是 getKeyFromBytes(byte[] src),需要在解密端使用。
import javax.crypto.SecretKey;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
.
.
.
public SecretKey generateRandomKey()
{
try {
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecretKey ret = keygen.generateKey();
return ret;
}
catch (Exception e) {
Log.warn(e.toString());
return null;
}
}
public SecretKey getKeyFromBytes(byte[] src)
{
try {
SecretKey ret = new SecretKeySpec(src, "AES");
return ret;
}
catch (Exception e) {
Log.warn(e.toString());
return null;
}
}
生成 IV 和 GCM 参数
当使用相同的密钥对相同的数据进行加密时,容易推测出相同的明文输出。为了防止这种情况发生,我们使用一个初始化向量(IV)。
在使用GCM时,IV由12个字节的随机数据(nonce)和4个字节的计数器生成。
计数器会自动处理,因此提供一个nonce来生成GCMParameterSpec。
在解密时需要使用nonce,因此也要将其存储为字节数组。IV可以被第三方知晓,因此即使与密文一起发送也没有问题。
import java.security.SecureRandom;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
.
.
.
public byte[] generateNonce()
{
try {
byte[] ret = new byte[12];
SecureRandom randgen = new SecureRandom();
randgen.nextBytes(ret);
return ret;
}
catch (Exception e) {
Log.warn(e.toString());
return null;
}
}
public GCMParameterSpec generateGCMParameter(byte[] nonce)
{
try {
GCMParameterSpec ret = new GCMParameterSpec(128, nonce);
return ret;
}
catch (Exception e) {
Log.warn(e.toString());
return null;
}
}
按需提供方案在GCM中,使用附加认证数据可以确认数据是否被篡改。
AAD可以是任意的字节数组数据,但在解密时也是必需的。
示例中,将“AADは追加認証データ、中身はなんでも良いが64kBytes以内にする”字符串使用UTF-8字节序列作为附加认证数据。
作为认证使用,它并不会提高加密强度。
加密
如果有明文、密钥和GCM参数,就可以进行加密。
AAD是可选的,可以选择不使用。如果不使用,不需要调用updateAAD。
import java.nio.charset.StandardCharsets;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.Cipher;
.
.
.
public byte[] encrypt(byte[] src, SecretKey key, GCMParameterSpec param)
{
byte[] aad = "AADは追加認証データ、中身はなんでも良いが64kBytes以内にする".getBytes(StandardCharsets.UTF_8);
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, param);
cipher.updateAAD(aad);
byte[] ret = cipher.doFinal(src);
return ret;
}
catch (Exception e) {
Log.warn(e.toString());
return null;
}
}
解密
如果有密文和密钥、GCM参数和AAD,就可以进行解密。
import java.nio.charset.StandardCharsets;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.Cipher;
.
.
.
public byte[] decrypt(byte[] src, SecretKey key, GCMParameterSpec param)
{
byte[] aad = "AADは追加認証データ、中身はなんでも良いが64kBytes以内にする".getBytes(StandardCharsets.UTF_8);
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, param);
cipher.updateAAD(aad);
byte[] ret = cipher.doFinal(src);
return ret;
}
catch (Exception e) {
Log.warn(e.toString());
return null;
}
}
总结
实际上,当进行加密和解密时,大致是这样的感觉。
// 暗号化
SecretKey key = getKeyFromPassword("P@ssW0rd");
byte[] nonce = generateNonce(); // 復号化で必要なので取っておく
GCMParameterSpec param = generateGCMParameter(nonce);
byte[] encdata = encrypt(srcdata, key, param);
// 復号化
SecretKey key = getKeyFromPassword("P@ssW0rd");
byte[] nonce = <取っておいたbyte配列>;
GCMParameterSpec param = generateGCMParameter(nonce);
byte[] decdata = decrypt(encdata, key, param);
import java.security.SecureRandom;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
.
.
.
public byte[] generateNonce()
{
try {
byte[] ret = new byte[12];
SecureRandom randgen = new SecureRandom();
randgen.nextBytes(ret);
return ret;
}
catch (Exception e) {
Log.warn(e.toString());
return null;
}
}
public GCMParameterSpec generateGCMParameter(byte[] nonce)
{
try {
GCMParameterSpec ret = new GCMParameterSpec(128, nonce);
return ret;
}
catch (Exception e) {
Log.warn(e.toString());
return null;
}
}
AAD可以是任意的字节数组数据,但在解密时也是必需的。
示例中,将“AADは追加認証データ、中身はなんでも良いが64kBytes以内にする”字符串使用UTF-8字节序列作为附加认证数据。
作为认证使用,它并不会提高加密强度。
加密
如果有明文、密钥和GCM参数,就可以进行加密。
AAD是可选的,可以选择不使用。如果不使用,不需要调用updateAAD。
import java.nio.charset.StandardCharsets;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.Cipher;
.
.
.
public byte[] encrypt(byte[] src, SecretKey key, GCMParameterSpec param)
{
byte[] aad = "AADは追加認証データ、中身はなんでも良いが64kBytes以内にする".getBytes(StandardCharsets.UTF_8);
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, param);
cipher.updateAAD(aad);
byte[] ret = cipher.doFinal(src);
return ret;
}
catch (Exception e) {
Log.warn(e.toString());
return null;
}
}
解密
如果有密文和密钥、GCM参数和AAD,就可以进行解密。
import java.nio.charset.StandardCharsets;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.Cipher;
.
.
.
public byte[] decrypt(byte[] src, SecretKey key, GCMParameterSpec param)
{
byte[] aad = "AADは追加認証データ、中身はなんでも良いが64kBytes以内にする".getBytes(StandardCharsets.UTF_8);
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, param);
cipher.updateAAD(aad);
byte[] ret = cipher.doFinal(src);
return ret;
}
catch (Exception e) {
Log.warn(e.toString());
return null;
}
}
总结
实际上,当进行加密和解密时,大致是这样的感觉。
// 暗号化
SecretKey key = getKeyFromPassword("P@ssW0rd");
byte[] nonce = generateNonce(); // 復号化で必要なので取っておく
GCMParameterSpec param = generateGCMParameter(nonce);
byte[] encdata = encrypt(srcdata, key, param);
// 復号化
SecretKey key = getKeyFromPassword("P@ssW0rd");
byte[] nonce = <取っておいたbyte配列>;
GCMParameterSpec param = generateGCMParameter(nonce);
byte[] decdata = decrypt(encdata, key, param);
import java.nio.charset.StandardCharsets;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.Cipher;
.
.
.
public byte[] encrypt(byte[] src, SecretKey key, GCMParameterSpec param)
{
byte[] aad = "AADは追加認証データ、中身はなんでも良いが64kBytes以内にする".getBytes(StandardCharsets.UTF_8);
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, param);
cipher.updateAAD(aad);
byte[] ret = cipher.doFinal(src);
return ret;
}
catch (Exception e) {
Log.warn(e.toString());
return null;
}
}
如果有密文和密钥、GCM参数和AAD,就可以进行解密。
import java.nio.charset.StandardCharsets;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.Cipher;
.
.
.
public byte[] decrypt(byte[] src, SecretKey key, GCMParameterSpec param)
{
byte[] aad = "AADは追加認証データ、中身はなんでも良いが64kBytes以内にする".getBytes(StandardCharsets.UTF_8);
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, param);
cipher.updateAAD(aad);
byte[] ret = cipher.doFinal(src);
return ret;
}
catch (Exception e) {
Log.warn(e.toString());
return null;
}
}
总结
实际上,当进行加密和解密时,大致是这样的感觉。
// 暗号化
SecretKey key = getKeyFromPassword("P@ssW0rd");
byte[] nonce = generateNonce(); // 復号化で必要なので取っておく
GCMParameterSpec param = generateGCMParameter(nonce);
byte[] encdata = encrypt(srcdata, key, param);
// 復号化
SecretKey key = getKeyFromPassword("P@ssW0rd");
byte[] nonce = <取っておいたbyte配列>;
GCMParameterSpec param = generateGCMParameter(nonce);
byte[] decdata = decrypt(encdata, key, param);
// 暗号化
SecretKey key = getKeyFromPassword("P@ssW0rd");
byte[] nonce = generateNonce(); // 復号化で必要なので取っておく
GCMParameterSpec param = generateGCMParameter(nonce);
byte[] encdata = encrypt(srcdata, key, param);
// 復号化
SecretKey key = getKeyFromPassword("P@ssW0rd");
byte[] nonce = <取っておいたbyte配列>;
GCMParameterSpec param = generateGCMParameter(nonce);
byte[] decdata = decrypt(encdata, key, param);