I have wrote a function to encrypt a string because it's a sensitive information. I was running some testes and I have identified that the generated output is different on each run.
I'm not sure if it's related to salt or something like that.
Is a normal behavior the output be different each time is called? Is it a good practice or a bad practice?
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);
}
}
If the encrypted data were the same every time the function was called, the encryption would be insecure.
Consider:
In any event, the first thing you do when you encrypt something using ECIES is to create a random ephemeral key. So that key will be different every time.
c1.init(Cipher.ENCRYPT_MODE, publicKey, new SecureRandom());
In theory, you could cause the result to be the same every time by generating the "random" key deterministically from the message and the recipient public key using a hash function along with some secret information such as the private key.
Update:
It is critical that an attacker not be able to predict or determine the ephemeral private key used in the encryption process. The usual way to do this is to use a secure random number generator, as you did in your code.
If you want the same input to produce the same output, you need to generate the ephemeral private key in a way that ensures the same result for the same input but also ensures an attacker can't predict or guess the ephemeral private key.
One way to meet both requirements is to use a hash of the recipient's public key, the sender's private key, and the message as the ephemeral private key. An attacker can't guess or predict this because they don't know the sender's private key. It will be the same so long as both keys and the message are the same, so it will ensure the same message sent by the same sender to the same recipient produces the same ephemeral private key and thus the same result.
Note that ECIES doesn't require the sender to have a private key. You can just use some sender identifier that is kept secret (it must be long enough to be secure, so treat it as a private key!). So long as the sender identifier stays the same, the same message sent to the same recipient will produce the same encrypted output.