如何在Java中将密钥接口用于秘密密钥?

时间:2019-12-02 06:27:51

标签: java cryptography

我有一个来自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接口。

如何使用它?

1 个答案:

答案 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是用于SecretKeyPublicKeyPrivateKey的通用父接口。它可以在许多表示加密算法的类中使用,因为它们可以与任何类型的密钥一起使用。例如Cipher既可以用于RSA,也可以用于AES。因此,实现只是在运行时检查是否给出了正确的密钥。

对于Mac,它可能也应该是SecretKey,因为Mac实际上始终是对称算法(Mac的非对称形式称为{{1 }} 毕竟)。但是,仅凭HMAC密钥是不够的,因为还有Signature个基于分组密码的算法,例如AES(因此需要Mac和算法SecretKey)。

为方便起见,"AES"还实现了SecretKeySpec;这样,您就不需要SecretKey来创建SecretKeyFactory。 Java设计人员忘记了不需要所需的硬件支持(例如工厂),但是我们就在这里。