用Java进行加密

首先最近,我觉得使用AES256/GCM应该没问题,下面是使用方法:
输入一个字节序列,获取加密后的字节序列,然后解密并获取字节序列。
如果在Java中使用Cipher,GCM标签似乎包含在密文中。因此,在使用其他库(如.NET)进行解密时,似乎需要单独提取标签。

暗号键可以用中文母语进行改述:密码键。如果选择AES256,请确保密钥长度为256位。
如果要输入密码进行解密,则可以将密码的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);

bannerAds