在数据库中存储私钥、盐和初始化向量的最佳实践是什么?

信息安全 加密 公钥基础设施 AES 爪哇 攻击预防
2021-08-18 04:46:44

我正在开发 Java 应用程序来创建数字签名来签署文档。我已经使用(椭圆曲线密码学)ECC 创建了私钥和公钥,现在我想将这两个密钥都存储在 mysql 数据库中。

我对公钥没有任何问题,但我关心的是安全地存储私钥。

我正在使用 AES 128 位加密来自用户的私钥和 6 位密码以存储在数据库中。要加密它,我还需要生成盐和初始化向量。

但是现在,要解密它,我还需要知道我用来解密加密私钥的相同盐和初始化向量 (iv)。

问题一:

使用私钥将盐和向量存储在数据库同一张表中是否安全?目前我对公钥和私钥、salt 和 iv 进行编码,并在数据库中保存为字符串(数据类型 VARCHAR)。

问题2:

以下是我设计的当前工作流程。这是最佳做法吗?我不想做太多会影响 CPU 使用率的进程。

创建密钥

  • 使用 ECC 创建私钥
  • 编码私钥(字节到字符串)
  • 使用 AES 加密私钥
  • 编码私钥,盐,iv(字节到字符串)
  • 作为 VARHCAR 存储在数据库中

签署文件

  • 从数据库中检索私钥
  • 解码私钥、盐、iv
  • 解密私钥
  • 解码私钥
  • 将解码的密钥转换为实际的私钥以进行签名

生成密钥和加密的代码片段:

        //Generate salt
        Random r = new SecureRandom();
        byte[] salt = new byte[8];
        r.nextBytes(salt);
        //System.out.println("salt: "+salt);

        //initialize vector
        byte[] vector = new byte[128/8];
        r.nextBytes(vector);
        IvParameterSpec ivspec = new IvParameterSpec(vector);
        //System.out.println("iv: "+iv);

        //initialize variables
        String MsgToEncrypt = encodedECCprivateKeyBytes;
        String userPin = ParamUtil.getString(actionRequest, "userPin");
        Cipher ecipher;

        //Generating AES key
        SecretKeyFactory factory =  SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec mykeySpec = new PBEKeySpec(userPin.toCharArray(), salt, 10000, 128);
        SecretKey tmp = factory.generateSecret(mykeySpec);
        SecretKeySpec mySecretkey = new SecretKeySpec(tmp.getEncoded(), "AES");

        //==> Create and initiate encryption
        System.out.println("Initiate encryption alogrithm...");
        ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        //System.out.println("Algorithm to encrypt private key: " + ecipher);
        ecipher.init(Cipher.ENCRYPT_MODE, mySecretkey, ivspec);
        //System.out.println("SecKey: "+mySecretkey);
2个回答

我将分两部分回答您的问题,因为您分两部分提出:

问题1:使用私钥将salt和vector存储在数据库同一张表中是否安全?目前我对公钥和私钥、salt 和 iv 进行编码,并在数据库中保存为字符串(数据类型 VARCHAR)。

简短的回答:是的,它是安全的。

说明:人们普遍认为,在散列密码旁边以纯文本形式保存 salt 是安全的。这是基于这样的逻辑:如果攻击者拥有盐和散列,他们仍然需要利用暴力攻击来尝试猜测用于创建散列的密码。将盐保密也可以提高安全性,但只要密码足够强,就没有必要。对于 IV,它要复杂得多,并且在我看来超出了此处的范围(并且超出了我的舒适区来解释),但是将 IV 以明文形式存储在密码旁边被认为是安全的-文本。

问题2:以下是我设计的当前工作流程。这是最佳做法吗?我不想做太多会影响 CPU 使用率的进程。

工作流程对我来说似乎很好,并且与我所做的大致相同,我认为您可以改进一些细节,因为您要求最佳实践:

  1. 首先,你的盐有点太短了。这样做的问题是,即使使用加密随机生成器生成 8 字节盐(就像你一样,这很好),也有可能发生冲突,两个或多个不同的用户被分配相同的盐值。为了缓解这种情况,我遵循的建议是 256 位或 32 字节的盐长度。

  2. 第二条建议,如果您已经知道,我不确定是否基于您的代码,但请确保您永远不要重复使用相同的 IV 和相同的 AES 密钥两次。由于非常花哨的密码分析,这可能会在加密中产生漏洞,使其易于破解。由于密钥是从我假设很少更改的用户密码生成的,因此请确保每次重新加密私钥时都轮换 IV。

  3. 看来您目前只进行了 10 000 轮散列。虽然这并不可怕,但我一直遵循的黄金法则是 2^(year-2000) 迭代。由于是 2018 年,最好的做法是在遇到严重的性能问题之前进行 250 000 次散列迭代或尽可能接近这个值。

否则我认为一切看起来都不错,您在散列和公私钥算法方面做出了不错的选择,并且明智地寻求帮助以确保您没有忽略任何事情。

希望能帮助到你!

是的。将 Salt 和 IV 存储在加密条目旁边是安全的。

要了解原因,您需要深入了解 Salt 和 IV 的用途。

盐是一种制作方式,这样如果一个条目被破解,它就不会渗透到任何其他条目。如果你有一张未经散列的加密密码表,一旦有人破解了一条记录,他们也会同时破解所有共享相同解密密码的记录。Salt并不是为了让暴力破解变得更加困难。它只是为了保证每个条目在加密方面与其他所有条目都是隔离的。因此,攻击者是否知道任何给定记录的盐条目并不重要——只要您为每个条目使用不同的盐,盐就可以完成它的工作。

同样,IV 旨在阻止已知的明文攻击。它的目的是防止有人利用“这些 X 条目在字节 XY 中都共享相似的内容”并使用密码分析来推断密钥的各个方面。IV 在加密之前基本上会混淆数据。因此,知道 IV 也无助于攻击者。

因此,继续将 IV 和 Salt 与加密值一起存储。