当前位置: 首页 > 知识库问答 >
问题:

如何保护我的Java AES加密密钥

吕嘉赐
2023-03-14

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

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

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

我该如何处理这些问题?

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.不要使用不安全或弱密码算法

共有3个答案

顾宏朗
2023-03-14

我最好的答案是在运行时注入密钥。我的系统在库伯内特斯使用Spring启动。因此,你可以使用库伯秘密来保存密钥,并在应用程序通过环境启动时注入它。坏演员通过某种方式黑进应用程序来获得密钥的唯一方法。此外,微服务的美妙之处在于你可以单独创建加密/解密微服务,并使用健壮的语言和库。也许吧,Rust?

冯星阑
2023-03-14

简而言之:如果你在保护你的密钥免受逆向工程的攻击,那么你做不了什么,几乎每一个安全系统都可以在一段时间后被破解。但是你可以通过使用嵌套加密算法和公开技术让黑客很难破解。你可以试试:

  1. 在运行时生成密钥。
  2. 将密钥分割并存储为不同位置的部件。例如。服务器和设备。
  3. 2FA双因素认证。
  4. 让密钥在被破解前就过期
  5. 通过ObSecurity的安全。

最后,请记住,每一种安全都可能被破解,而你只能让它们更难破解。

韦安怡
2023-03-14

你有几个选择来做这件事。

如果确实需要加密密钥,可以使用JCEKS密钥库以加密形式存储密钥。这个密钥库非常适合存储单个密钥,或者说可以使用它来存储单个密钥。你可以在这篇文章中看到一个如何使用它的例子。它将给你一个关于兜帽下发生的事情的解释,并将为你提供一些关于这种密钥库的背景信息。

您也可以通过使用基于密码的加密html" target="_blank">算法直接加密您的密钥。这个堆栈溢出问题将为您提供一个实现这种解决方案的好例子。

如果您的需求只是在属性文件中加密和解密属性,正如您在注释中指出的那样,您可以使用Jasypt。这个库实际上是几个加密解决方案的包装器。

正如您可以在他们的留档中看到的,这个库将为您提供一些通用用例的实用程序。

考虑下面的例子,从上面的链接中得到,非常适合你的需要。它们提供了一个属性文件:

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,它还将为您提供一个很好的框架集成。

正如您所见,与所采用的解决方案无关,您始终需要一个真正保护密钥的密码。

为上述不同机制提供此密码的最佳方法是将其定义为环境变量:其想法是,此信息仅对系统管理员和负责维护服务器软件的it人员可见。

另一方面,所有主要的云提供商(AWS、GCP、Azure...)将为您提供服务(前两个是KMS,Azure是Key Vault),允许您在没有访问实际密钥的情况下安全加密和解密信息:在这些场景中,使用这些服务可能是更好的方法。

 类似资料:
  • 我有一个应用程序,需要在配置文件中存储一些秘密密码,如数据库和ftp密码/详细信息。我环顾四周,发现了许多使用AES的加密/解密解决方案,但我似乎不知道如何在不改变密钥的情况下使其工作。这意味着我可以加密和解密(使用相同的秘密密钥),但在重启等过程中保持持久性。我似乎无法让秘密钥匙保持不变。下面的示例显示了我的工作方法: 到目前为止还不错。然而,如果我运行它一次,我可能会得到'2Vhht/L80U

  • 问题内容: 前一段时间,在我的工作中,我需要保护某些类以防他人读取代码。为此,我创建了一个EncryptedClassLoader,它加载了以前加密的类,并且还可以加载普通(未加密)的类。以这种方式工作有点复杂,并且也要进行测试(编译,然后加密,然后解密)。 是否有任何免费的框架可以满足我的需求,并且易于处理?我的意思是,不仅混淆而且还加密文件,因此没有人可以读取或调试该部分代码。我可以轻松更改用

  • protection([string $password]); 示例一 $config = ['path' => './tests']; ​ $fileObject = new \Vtiful\Kernel\Excel($config); $fileObject = $fileObject->fileName('tutorial.xlsx'); ​ $filePath = $fileObject

  • 问题内容: 我是Java初学者。我正在开发可解密某些数据的应用程序。解密密钥被硬编码到软件中,因此可以通过分析字节码看到。 我知道不能完全防止逆向工程,所以我想做的就是使过程尽可能地艰巨。 我的想法不是直接将密钥放入代码中,而是将其进行某种转换。例如,我可以写- 这样,查看字节码的人无法立即读取它。但是将必须遵循逻辑并对其进行转换,这在字节级别上不会那么容易。 那么你们怎么看?这有用吗?除了十六进

  • 我试图保护Spring BootAPIendpoint。我希望只传递api密钥和秘密作为头的一部分来调用api。我尝试了这个链接中发布的代码。但是在头中使用授权调用api时拒绝访问。使用API key和secret保护Spring Boot API我想知道我应该作为头部的一部分传递什么,这样我就可以从API获得成功的响应。我做了以下步骤:我在application.properties中添加了以下

  • PEMException:创建加密私钥时出现问题:System.NullReferenceException:对象引用未设置为对象的实例。在org.bouncycastle.openssl.pemreader.readprivateKey(PemObject PemObject) 下面是Decrypt方法的代码: