java - 如何保护我的 Java AES 加密 key

标签 java encryption

我正在实现一个使用 AES 加密来加密字符串的程序,并希望将我的“ secret key ”保存在一个文件中,而不是在源代码中对其进行硬编码。

但是,它给我带来了一个问题,我如何保护这个 key 不被其他人看到?

如果我再次加密这个“keyFile”,我将不得不再次处理同样的问题。

我该如何处理此类问题?

String keyFile = ...;
byte[] keyb = Files.readAllBytes(Paths.get(keyFile));
SecretKeySpec skey = new SecretKeySpec(keyb, "AES");



import java.util.Arrays;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;

class Msc61 {
    public static SecretKey generateKey() {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(128);
            return kgen.generateKey();
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException(e.toString());
        }
    }

    public static byte[] encrypt_cbc(SecretKey skey, String plaintext) {
        /* Precond: skey is valid; otherwise IllegalStateException will be thrown. */
        try {
            byte[] ciphertext = null;
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");           
            final int blockSize = cipher.getBlockSize();
            byte[] initVector = new byte[blockSize];
            (new SecureRandom()).nextBytes(initVector);
            IvParameterSpec ivSpec = new IvParameterSpec(initVector);
            cipher.init(Cipher.ENCRYPT_MODE, skey, ivSpec);
            byte[] encoded = plaintext.getBytes(java.nio.charset.StandardCharsets.UTF_8);
            ciphertext = new byte[initVector.length + cipher.getOutputSize(encoded.length)];
            for (int i=0; i < initVector.length; i++) {
                ciphertext[i] = initVector[i];
            }
            // Perform encryption
            cipher.doFinal(encoded, 0, encoded.length, ciphertext, initVector.length);
            return ciphertext;
        } catch (NoSuchPaddingException | InvalidAlgorithmParameterException | ShortBufferException |
            BadPaddingException | IllegalBlockSizeException | InvalidKeyException | NoSuchAlgorithmException e)
        {
            /* None of these exceptions should be possible if precond is met. */
            throw new IllegalStateException(e.toString());
        }
    }

    public static String decrypt_cbc(SecretKey skey, byte[] ciphertext)
        throws BadPaddingException, IllegalBlockSizeException /* these indicate corrupt or malicious ciphertext */
    {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");           
            final int blockSize = cipher.getBlockSize();
            byte[] initVector = Arrays.copyOfRange(ciphertext, 0, blockSize);
            IvParameterSpec ivSpec = new IvParameterSpec(initVector);
            cipher.init(Cipher.DECRYPT_MODE, skey, ivSpec);
            byte[] plaintext = cipher.doFinal(ciphertext, blockSize, ciphertext.length - blockSize);
            return new String(plaintext);
        } catch (NoSuchPaddingException | InvalidAlgorithmParameterException |
            InvalidKeyException | NoSuchAlgorithmException e)
        {
            /* None of these exceptions should be possible if precond is met. */
            throw new IllegalStateException(e.toString());
        }
    }
}

引用:https://wiki.sei.cmu.edu/confluence/display/java/MSC61-J.+Do+not+use+insecure+or+weak+cryptographic+algorithms

最佳答案

您有多种选择来执行此操作。
如果您确实需要加密您的 key ,您可以使用 JCEKS keystore 以加密形式存储它。这个 keystore 非常适合,或者更好地说,可以用于存储单个 key 。您可以在此 DZone article 中看到如何使用它的示例.它将向您解释幕后发生的事情,并为您提供有关此类 keystore 的一些背景知识。
您还可以使用基于密码的加密算法直接加密您的 key 。 This stackoverflow question将为您提供一个很好的例子来实现这种解决方案。
如果您的要求只是如您在评论中指出的那样加密和解密属性文件中的属性,您可以使用 Jasypt .这个库实际上是几个加密解决方案的包装。
正如您在他们的 documentation 中看到的那样,本库将为您提供 some utilities对于一般用例。
考虑从上面的链接获得的以下示例,非常适合您的需求。他们提供了一个属性文件:

datasource.driver=com.mysql.jdbc.Driver
datasource.url=jdbc:mysql://localhost/reportsdb
datasource.username=reportsUser
datasource.password=ENC(G6N718UuyPE5bHyWKyuLQSm02auQPUtm)
他们展示了如何读取这些属性:
/*
  * First, create (or ask some other component for) the adequate encryptor for
  * decrypting the values in our .properties file.
  */
 StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
 encryptor.setPassword("jasypt"); // could be got from web, env variable...
 encryptor.setAlgorithm("PBEWithHMACSHA512AndAES_256");
 encryptor.setIvGenerator(new RandomIvGenerator());
 
 /*
  * Create our EncryptableProperties object and load it the usual way.
  */
 Properties props = new EncryptableProperties(encryptor);
 props.load(new FileInputStream("/path/to/my/configuration.properties"));

 /*
  * To get a non-encrypted value, we just get it with getProperty...
  */
 String datasourceUsername = props.getProperty("datasource.username");

 /*
  * ...and to get an encrypted value, we do exactly the same. Decryption will
  * be transparently performed behind the scenes.
  */ 
 String datasourcePassword = props.getProperty("datasource.password");

 // From now on, datasourcePassword equals "reports_passwd"...
如果你使用的是 Spring,它也会给你一个 great framework integration .
如您所见,无论采用何种解决方案,您始终需要一个真正保护您的 key 的密码。
将此密码提供给上述不同机制的最佳方法是将其定义为环境变量:这个想法是只有系统管理员和负责维护服务器软件的 IT 人员才能看到此信息。
另一方面,所有主要的云提供商(AWS、GCP、Azure 等)都会为您提供服务(前两个是 KMS,Azure 是 Key Vault),即使没有访问实际 key :在这些场景中,使用这些服务可能是更好的方法。

关于java - 如何保护我的 Java AES 加密 key ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55144526/

相关文章:

java - 将 then 与 Mono<Void> 一起使用时出现意外行为

java - C#.net 到 Java-使用带密码的 AES 加密和解密

java - 如何以编程方式添加 EJB 拦截器?

java - 使用深层数组创建 JSON 文件

java - 在 Java 中创建 Set 的子集

security - 我应该如何创建我的 DES key ?为什么 7 个字符的字符串不够用?

Java-HMACSHA256

android - Glide Images的DiskCache加解密?

php - 我是否需要在 php header 中进行特殊设置才能生成 SSL 加密的 xml 输出?

Java:对象数组中的对象数组