我有一个来自creating base 64 hashes的代码
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class ApiSecurityExample {
public static void main(String[] args) {
try {
String secret = "secret";
String message = "Message";
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
String hash = Base64.encodeBase64String(sha256_HMAC.doFinal(message.getBytes()));
System.out.println(hash);
}
catch (Exception e){
System.out.println("Error");
}
}
}
secret_key
中有sha256_HMAC.init(secret_key);
当我阅读时,它会告诉您使用Key key
接口。
如何使用它?
答案 0 :(得分:4)
该示例做错了,因为不应使用字符串来存储密钥。
秘密密钥应由对手无法预测的字节组成。生成这些密码的最合乎逻辑的方法是使用随机数生成器,但是您也可以使用其他密钥,棘轮和许多其他方式上的密钥推导函数从密钥建立(Diffie-Hellman)生成它们。
一种比较危险的方法是从密码生成密码。为此,通常使用基于密码的密钥派生功能或PBKDF。 Java直接支持PBKDF2,可用于此目的。
因此您可以通过以下方式创建HMAC密钥:
Mac mac = Mac.getInstance("HMACSHA256");
SecureRandom rng = new SecureRandom();
// key size can be anything but should default to the hash / MAC output size for HMAC
byte[] hmacKeyData = new byte[mac.getMacLength()];
rng.nextBytes(hmacKeyData);
SecretKey hmacKey = new SecretKeySpec(hmacKeyData, "HMACSHA256");
Arrays.fill(hmacKeyData, (byte) 0x00);
但是,以下代码较短,可能更具描述性。它还允许以后使用硬件设备来实现Mac
,尽管这可能会超出您的范围。
KeyGenerator kg = KeyGenerator.getInstance("HMACSHA256");
SecretKey hmacKey = kg.generateKey();
最后,如果您仍然想使用密码,请使用PKBDF2,不要忘记存储盐:
// you don't want to use a string, as you cannot delete strings in Java
char[] password = {'p', 'a', 's', 's' };
SecureRandom rng = new SecureRandom();
byte[] salt = new byte[128 / Byte.SIZE];
rng.nextBytes(salt);
int iterations = 1_000_000;
Mac mac = Mac.getInstance("HMACSHA256");
PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, mac.getMacLength() * Byte.SIZE);
SecretKeyFactory pbkdf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hmacKeyData = pbkdf.generateSecret(spec).getEncoded();
SecretKey hmacKey = new SecretKeySpec(hmacKeyData, "HMACSHA256");
// clean up secret material
Arrays.fill(password, (char) 0x0000);
spec.clearPassword();
Arrays.fill(hmacKeyData, (byte) 0x00);
由于如果攻击者具有可比较结果的MAC,则攻击者可能永远需要尝试输入密码,因此,选择一个非常复杂的密码是一个很好的主意;这就是为什么基于密码的加密通常不是一个好主意的原因。
Key
是用于SecretKey
,PublicKey
和PrivateKey
的通用父接口。它可以在许多表示加密算法的类中使用,因为它们可以与任何类型的密钥一起使用。例如Cipher
既可以用于RSA,也可以用于AES。因此,实现只是在运行时检查是否给出了正确的密钥。
对于Mac
,它可能也应该是SecretKey
,因为Mac
实际上始终是对称算法(Mac
的非对称形式称为{{1 }} 毕竟)。但是,仅凭HMAC密钥是不够的,因为还有Signature
个基于分组密码的算法,例如AES(因此需要Mac
和算法SecretKey
)。
为方便起见,"AES"
还实现了SecretKeySpec
;这样,您就不需要SecretKey
来创建SecretKeyFactory
。 Java设计人员忘记了不需要所需的硬件支持(例如工厂),但是我们就在这里。