为什么每次调用 ECIES 加密时都会给出不同的值?

Why does ECIES encryption gives a different value each time is called?

我写了一个函数来加密一个字符串,因为它是一个敏感信息。我正在 运行 一些睾丸,我发现每个 运行 上生成的输出都不同。

我不确定它是否与盐或类似的东西有关。

每次调用输出不同是正常行为吗?这是好习惯还是坏习惯?

import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import javax.crypto.Cipher;
import java.io.BufferedReader;
import java.io.FileReader;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;


public class App {

    private static String PUBLIC_PEM = "/opt/public.pem";

    static String TO_ENCODE = "not going well";

    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        App app = new App();

        PublicKey publicKey = app.getECPublicKeyFromPEM(PUBLIC_PEM);

        byte[] message = TO_ENCODE.getBytes();

        byte[] out1;

        Cipher c1 = Cipher.getInstance("ECIES");

        c1.init(Cipher.ENCRYPT_MODE, publicKey, new SecureRandom());

        out1 = c1.doFinal(message, 0, message.length);

        System.out.println(Base64.toBase64String(out1));
    }

    public ECPublicKey getECPublicKeyFromPEM(String publicPem) throws  Exception {
        PEMParser pemParser = getPemFile(publicPem);
        SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(pemParser.readObject());

        return (ECPublicKey) new JcaPEMKeyConverter().getPublicKey(subjectPublicKeyInfo);
    }

    private PEMParser getPemFile(String path) throws Exception {
        BufferedReader reader = new BufferedReader(new FileReader(path));
        return new PEMParser(reader);
    }
}

如果每次调用函数时加密的数据都相同,则加密不安全。

考虑:

  1. 你要发消息,“攻击”或“不要攻击”。
  2. 攻击者拦截了消息。
  3. 攻击者后来观察到收件人没有攻击。
  4. 攻击者拦截了下一条消息。
  5. 攻击者将该消息与之前的消息进行比较。如果它们相同,攻击者就会知道消息说“请勿攻击”。如果它们不相同,攻击者就会知道消息说“攻击”。

无论如何,当您使用 ECIES 加密某些内容时,您要做的第一件事就是创建一个随机的临时密钥。所以那个密钥每次都会不同。

    c1.init(Cipher.ENCRYPT_MODE, publicKey, new SecureRandom());

理论上,您可以通过使用散列函数从消息和收件人 public 密钥确定性地生成“随机”密钥以及一些秘密信息(例如私钥。

更新:

重要的是攻击者不能预测或确定加密过程中使用的临时私钥。通常的方法是使用安全的随机数生成器,就像您在代码中所做的那样。

如果您希望相同的输入产生相同的输出,您需要以一种方式生成临时私钥,以确保对相同输入产生相同的结果,同时也确保攻击者无法预测或猜测临时私钥私钥。

满足这两个要求的一种方法是使用收件人的 public 密钥、发件人的私钥和消息的散列作为临时私钥。攻击者无法猜测或预测这一点,因为他们不知道发件人的私钥。只要密钥和消息相同,它就会相同,因此它将确保同一发送者向同一接收者发送的同一消息产生相同的临时私钥,从而产生相同的结果。

请注意,ECIES 不要求发件人拥有私钥。您可以只使用一些保密的发件人标识符(它必须足够长才能安全,因此将其视为私钥!)。只要发件人标识符保持不变,发送给同一收件人的同一消息将产生相同的加密输出。