我实现了一个简单的Java实用程序类,使用aes/gcm/nopadding进行加密和解密。我使用这段代码:
public byte[] encrypt(byte[] input, byte[] key, byte[] iv) throws Exception{
Cipher cipher = initAES256GCMCipher(key, iv, Cipher.ENCRYPT_MODE);
return cipher.doFinal(input);
}
public byte[] decrypt(byte[] input, byte[] key, byte[] iv) throws Exception{
Cipher cipher = initAES256GCMCipher(key, iv, Cipher.DECRYPT_MODE);
return cipher.doFinal(input);
}
private Cipher initAES256GCMCipher(byte[] key, byte[] iv, int encryptionMode) throws Exception{
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, iv);
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(encryptionMode, secretKey, gcmParameterSpec);
return cipher;
}
IV总是一个12字节数组,key是32字节数组,SecureRandom使用种子生成。我知道在不同的OS上SecureRandom是不同的,但是加密和解密是在同一个OS上执行的,所以应该没有问题。
是不是很线性,对吧?它在Windows上工作得很好,加密和破译返回相同的文本。然而,在Docker映像上,相同的JAR不起作用:加密工作正常,但解密抛出“AeadBadTagException”。
不要使用securerandom
派生密钥。您可以使用键派生函数(KDF)来实现这一点,比如HKDF。但老实说,您必须想出一种方法来通信密钥--不使用securerandom
。
问题是-相对安全的-SHA1PRNG算法没有很好地定义。SUN提供者确实接受一个种子,然后将它用作唯一的种子,如果您在从中检索任何随机数据之前播种它。但是,其他提供程序只会将种子混合到底层CSPRNG的状态,这是有意义的。这也是大多数其他SecureRandom
实现的默认值。
很少有SecureRandom
实现完全指定随机比特的返回方式,即使指定了基础算法(使用DRBG)。如果您只是期望随机值,这通常不是问题。但是,如果您使用它作为确定性算法,例如KDF(或哈希函数),那么这就成为一个问题,您可能会为相同的输入获得不同的键。
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import hex.Hex;
public class PoorMansKDF {
public interface KeyDerivationParameters extends AlgorithmParameterSpec {
String getDerivedKeyAlgorithm();
int getDerivedKeySize();
byte[] getCanonicalInfo();
}
public static class KeyDerivationParametersWithLabel implements KeyDerivationParameters {
private final String algorithm;
private final int keySize;
private final String label;
public KeyDerivationParametersWithLabel(String algorithm, int keySize, String label) {
this.algorithm = algorithm;
this.keySize = keySize;
this.label = label;
}
@Override
public byte[] getCanonicalInfo() {
if (label == null) {
// array without elements
return new byte[0];
}
return label.getBytes(StandardCharsets.UTF_8);
}
@Override
public String getDerivedKeyAlgorithm() {
return algorithm;
}
@Override
public int getDerivedKeySize() {
return keySize;
}
}
private enum State {
CONFIGURED,
INITIALIZED;
}
public static PoorMansKDF getInstance() throws NoSuchAlgorithmException {
return new PoorMansKDF();
}
private final Mac hmac;
private State state;
private PoorMansKDF() throws NoSuchAlgorithmException {
this.hmac = Mac.getInstance("HMACSHA256");
this.state = State.CONFIGURED;
}
public void init(Key key) throws InvalidKeyException {
if (key.getAlgorithm().equalsIgnoreCase("HMAC")) {
key = new SecretKeySpec(key.getEncoded(), "HMAC");
}
hmac.init(key);
this.state = State.INITIALIZED;
}
public Key deriveKey(KeyDerivationParameters info) {
if (state != State.INITIALIZED) {
throw new IllegalStateException("Not initialized");
}
final int keySize = info.getDerivedKeySize();
if (keySize < 0 || keySize % Byte.SIZE != 0 || keySize > hmac.getMacLength() * Byte.SIZE) {
throw new IllegalArgumentException("Required key incompatible with this KDF");
}
final int keySizeBytes = keySize / Byte.SIZE;
// we'll directly encode the info to bytes
byte[] infoData = info.getCanonicalInfo();
final byte[] fullHMAC = hmac.doFinal(infoData);
final byte[] derivedKeyData;
if (fullHMAC.length == keySizeBytes) {
derivedKeyData = fullHMAC;
} else {
derivedKeyData = Arrays.copyOf(fullHMAC, keySizeBytes);
}
SecretKey derivedKey = new SecretKeySpec(derivedKeyData, info.getDerivedKeyAlgorithm());
Arrays.fill(derivedKeyData, (byte) 0x00);
if (fullHMAC != derivedKeyData) {
Arrays.fill(fullHMAC, (byte) 0x00);
}
return derivedKey;
}
// test only
public static void main(String[] args) throws Exception {
var kdf = PoorMansKDF.getInstance();
// input key (zero byte key for testing purposes, use your own 16-32 byte key)
// do not use a password here!
var masterKey = new SecretKeySpec(new byte[32], "HMAC");
kdf.init(masterKey);
// here "enc" is a label, in this case for a derived key for ENCryption
var labeledParameters = new KeyDerivationParametersWithLabel("AES", 256, "enc");
var derivedKey = kdf.deriveKey(labeledParameters);
// use your own hex decoder, e.g. from Apache Commons
System.out.println(Hex.encode(derivedKey.getEncoded()));
var aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
var gcmParams = new GCMParameterSpec(128, new byte[12]);
aesCipher.init(Cipher.ENCRYPT_MODE, derivedKey, gcmParams);
var ct = aesCipher.doFinal();
System.out.println(Hex.encode(ct));
}
}
我试图制作一个跨平台的JavaFX应用程序,它在Windows和OSX机器上工作得很好,但在Linux上不行。 jar是在Intellij思想中使用基本的JavaFX配置构建的。 有人帮忙吗?
我是android新手,我能够在kotlin中为我的应用程序设置firebase。如果我在Nexus 5X API 27 emulator中运行该应用程序,我就能够获取数据库,但当我在实际设备三星S5(Google play Services V 12.5.29,android V 5.0)中运行该应用程序时,我无法获得addValueEventListener回调。 Gradle文件: 我知道这
我有一段非常简单的Java代码,在那里我尝试从Java连接到我的Oracle DB。 在Windows下一切正常,但当我尝试在Ubuntu上运行时,我得到了一个错误。 我读了很多书,也试过很多解决方法。这是我的代码: 当我运行它时,我收到一个错误: 连接失败Java.sql.sqlRecoverable异常:IO错误:网络适配器无法在oracle.jdbc.driver.T4CConnection
结果在Windows和Linux之间有所不同。 Linux:鼠标位置:0,0 Windows:鼠标位置:623.0,367.0 我不知道为什么它不能在windows上工作,甚至似乎与lwjgl版本完全无关,因为我尝试了3.1.6、3.2.1、3.2.2和3.2.3-snapshot,所有这些版本都是一样的。所以问题要么是我在创建窗口时忘记了一些东西,要么是windows在某个更新中损坏了一些东西,
你好,我正在制作一个Java多人游戏,一切都很好。它具备了所有用户需要的功能,但我发现了一个问题,它使用ServerSocket作为服务器,Socket作为客户端,工作正常,但最大的问题是,它在全球范围内都不起作用。只有兰,我甚至试过Hamachi,但也没用。 你有什么想法吗? 更多信息:我使用服务器中的特定线程来接受、发送和接收套接字,还使用客户端中的特定线程来发送和接收套接字。它发送一个我制作
与/user/login?_format=json完全相同的POST在Postman中不起作用(错误403禁止),但在curl中使用相同的参数可以正常工作。 这个Curl的工作原理是:Curl--header“content-type:application/json”--request POST--data“{”name“:”bobo“,”pass“:”xxx“}”http://[domain]