Can we encrypt large files with ECC or it is like RSA works for small only? can anyone please recommend a good website for ECC Java implementation.
Thanks
In general you are required to perform hybrid encryption with ECC. ECIES for instance is basically a key agreement followed by symmetric encryption. So you cannot directly encrypt anything with ECIES, which is the most common ECC method for encryption. Basically you should couple it to a symmetric cipher. This is actually the best scheme for RSA encryption as well, most of the time.
As you can see you can use this directly as a Cipher using CBC mode & PKCS#7 padding, but beware of the large header (117 bytes for a 384 curve, no less). This is required to perform the key derivation. Make sure that the public key is properly validated (I'm not sure about the Bouncy Castle code in this regards, haven't taken a look at it).
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator ecKeyGen = KeyPairGenerator.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
ecKeyGen.initialize(new ECGenParameterSpec("brainpoolP384r1"));
// doesn't work, which means we are dancing on the leading edge :)
// KeyPairGenerator ecKeyGen = KeyPairGenerator.getInstance("EC");
// ecKeyGen.initialize(new ECGenParameterSpec("secp384r1"));
KeyPair ecKeyPair = ecKeyGen.generateKeyPair();
System.out.println("What is slow?");
Cipher iesCipher = Cipher.getInstance("ECIESwithAES");
iesCipher.init(Cipher.ENCRYPT_MODE, ecKeyPair.getPublic());
byte[] ciphertext = iesCipher.doFinal(com.google.common.base.Strings.repeat("owlstead", 1000).getBytes());
iesCipher.init(Cipher.DECRYPT_MODE, ecKeyPair.getPrivate());
byte[] plaintext = iesCipher.doFinal(ciphertext);
System.out.println(Hex.toHexString(ciphertext));
System.out.println(new String(plaintext));
}
public static void main(String[] args) throws Exception
{
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator ecKeyGen = KeyPairGenerator.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
ecKeyGen.initialize(new ECGenParameterSpec("secp256r1"));
KeyPair ecKeyPair = ecKeyGen.generateKeyPair();
System.out.println("What is slow?");
Cipher iesCipher = Cipher.getInstance("ECIESwithAES-CBC");
Cipher iesDecipher = Cipher.getInstance("ECIESwithAES-CBC");
iesCipher.init(Cipher.ENCRYPT_MODE, ecKeyPair.getPublic());
String message = "Hello World";
byte[] ciphertext = iesCipher.doFinal(message.getBytes());
System.out.println(Hex.toHexString(ciphertext));
iesDecipher.init(Cipher.DECRYPT_MODE, ecKeyPair.getPrivate(), iesCipher.getParameters());
byte[] plaintext = iesDecipher.doFinal(ciphertext);
System.out.println(new String(plaintext));
}
Related
I have an issue with my java code. I'm trying to encrypt a file. However, when I run my java code I get "java.security.InvalidKeyException: Invalid AES key length: 162 bytes".
Here is the code:
byte[] rawFile;
File f = new File("./src/wonkybox.stl");
FileInputStream fileReader = new FileInputStream(f);
rawFile = new byte[(int)f.length()];
fileReader.read(rawFile);
/***** Encrypt the file (CAN DO THIS ONCE!) ***********/
//Generate the public/private keys
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("AES");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG","SUN");
keyGen.initialize(1024, random);
KeyPair key = keyGen.generateKeyPair();
PrivateKey privKey = key.getPrivate();
PublicKey pubKey = key.getPublic();
//Store the keys
byte[] pkey = pubKey.getEncoded();
FileOutputStream keyfos = new FileOutputStream("./CloudStore/keys/pubkey");
keyfos.write(pkey);
keyfos.close();
pkey = privKey.getEncoded();
keyfos = new FileOutputStream("./CloudStore/keys/privkey");
keyfos.write(pkey);
keyfos.close();
//Read public/private keys
KeyFactory keyFactory = KeyFactory.getInstance("AES");
FileInputStream keyfis = new FileInputStream("./CloudStore/keys/pubkey");
byte[] encKey = new byte[keyfis.available()];
keyfis.read(encKey);
keyfis.close();
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encKey);
PublicKey pub1Key = keyFactory.generatePublic(pubKeySpec);
keyfis = new FileInputStream("./CloudStore/keys/privkey");
encKey = new byte[keyfis.available()];
keyfis.read(encKey);
keyfis.close();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(encKey);
PrivateKey priv1key = keyFactory.generatePrivate(privKeySpec);
//Encrypt file using public key
Cipher cipher = Cipher.getInstance("AES");
System.out.println("provider= " + cipher.getProvider());
cipher.init(Cipher.ENCRYPT_MODE, pub1Key);
byte[] encryptedFile;
encryptedFile = cipher.doFinal(rawFile);
//Write encrypted file to 'CloudStore' folder
FileOutputStream fileEncryptOutput = new FileOutputStream(new File("./CloudStore/encrypted.txt"));
fileEncryptOutput.write(encryptedFile);
fileEncryptOutput.close();
The error occurs at the line "KeyPairGenerator keyGen = KeyPairGenerator.getInstance("AES");".
AES is a symmetric algorithm, hence they use of KeyPairGenerator is not supported. To generate a key with AES you call KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); //set keysize, can be 128, 192, and 256
By looking at the rest of your code, it looks like you are trying to achive asymmetric encryption (since you call getPublic() and getPrivate() etc), so I advice you to switch to using RSA or any other asymmetric algorithm that java supports. You will most likley only need to replace AES with RSA in your getInstance(); calls, and pherhaps some fine-tuning. Good luck
As far as I know, AES is symmetric encryption algorithm i.e. it needs only one key for encryption/decryption.
From the JavaDoc of java.security.KeyPairGenerator:
The KeyPairGenerator class is used to generate pairs of public and private keys.
Meaning that it should be used for asymmetric encryption algorithms. For symmetric encryption algorithms one should use javax.crypto.KeyGenerator.
However, I advise simply mimicking some tutorial on how to encrypt / decrypt byte array in Java using AES like this one.
It uses sun.misc.Base64Encoder / Base64Decoder classes to encode / decode byte array to / from String, however you may skip this step.
Hope this helps
How can you use a keypair generator for AES? AES is a symmetric key algorithm. Refer this link. That means if you encrypt data using a key "k", then you will have to decrypt it also using the same key "k". But when you generate key pair, as the name suggests, two keys are generated and if you encrypt using one of the keys, you can decrypt only using the other key. This is the base for PKI.
If you want to use keypair generator use an algorithm like "rsa" or "dsa" in the getInstance() method like this :
KeyPairGenerator keygen=KeyPairGenerator.getInstance("rsa");
I think your code should now work fine after making the above change.
Thank you for taking you time to assist me with this!
THIS POST HAS BEEN EDITED FOR LESS INFORMATION SEE THE EDITED PART
Well I have spend ours of research on this matter and I ended up with a working piece of code..
But Encryption is not a place to make mistakes, and I wanted to ask if my code is actualy secure! It's really important for me because I want to implement it to a program so my code is...
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
public class EncryptFile{
private static final String FILE_IN = "./EncryptFile.java";
private static final String FILE_ENCR = "./EncryptFile_encr.java";
private static final String FILE_DECR = "./EncryptFile_decr.java";
public static void main(String []args){
try
{
Encryption("passwordisnottheactual", Files.readAllBytes(Paths.get(FILE_IN)));
Decryption("passwordisnottheactual");
}catch(Exception e){
System.out.println(e.getMessage());
}
}
private static void Encryption(String Key, byte[] byteArray) throws Exception
{
// Decode the base64 encoded Key
byte[] decodedKey = Base64.getDecoder().decode(Key);
// Rebuild the key using SecretKeySpec
SecretKey secretKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
// Cipher gets AES Algorithm instance
Cipher AesCipher = Cipher.getInstance("AES");
//Initialize AesCipher with Encryption Mode, Our Key and A ?SecureRandom?
AesCipher.init(Cipher.ENCRYPT_MODE, secretKey, new SecureRandom());
byte[] byteCipherText = AesCipher.doFinal(byteArray);
//Write Bytes To File
Files.write(Paths.get(FILE_ENCR), byteCipherText);
}
private static void Decryption(String Key) throws Exception
{
//Ddecode the base64 encoded string
byte[] decodedKey = Base64.getDecoder().decode(Key);
//Rebuild key using SecretKeySpec
SecretKey secretKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
//Read All The Bytes From The File
byte[] cipherText = Files.readAllBytes(Paths.get(FILE_ENCR));
//Cipher gets AES Algorithm Instance
Cipher AesCipher = Cipher.getInstance("AES");
//Initialize it in Decrypt mode, with our Key, and a ?SecureRandom?
AesCipher.init(Cipher.DECRYPT_MODE, secretKey, new SecureRandom());
byte[] bytePlainText = AesCipher.doFinal(cipherText);
Files.write(Paths.get(FILE_DECR), bytePlainText);
}
}
EDIT
Possible duplicate of Simple Java AES encrypt/decrypt example – JFPicard
Well it could be but these answers Use IVParameterSpec and I wanted to know if
this line of code is actually secure or if it is bad practice:
AesCipher.init(Cipher.DECRYPT_MODE, secretKey, new SecureRandom());
because I use a new SecureRandom() every time,
and I haven't seen anyone use a SecureRandom object like this.
Encryption key
The password is passes as a string but the Encryption function Base64 decoded it, that is a coding error.
When a password is used the encryption key should be derived from it with the PBKDF2 (aka Rfc2898DeriveBytes) function.
When using key derivation the salt and iteration count needs to be available for decryption, often they are provided in a prefix to the encrypted data.
Encryption mode
No encryption mode is supplied.
Use CBC mode with a random IV.
Just prefix the encrypted data with the IV for use on decryption.
Padding
AES is a block cipher and as such requires the input data size to be a multiple of the block size.
Specify PKCS#7 (née PKCS#5) padding, it will add padding on encryption and remove it on decryption.
On decryption do not return "padding" errors, they can provide a "Padding Oracle" attack.
Explicit
Specify all encryption parameters and sizes.
Do not rely on implementation defaults.
Encryption authentication
Consider if there is a need to know if the data is decrypted correctly.
Versioning
Add a version indicator so that if changes are necessary later there is an compatibility path.
Or consider using RNCryptor which handles all this and more.
Update: (thx Andy for the comment)
If GCM mode is available and interoperability across platforms and libraries is not an issue GCM is arguably a better encryption mode. GCM has authentication and padding build-in making it more robust and an easier secure solution.
Encryption and Decryption successful when encrypt with public key and decrypt with private key :
C# encryption with public key(Successful)
public string EncryptData(string data) {
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(xml); //public key
var cipher = rsa.Encrypt(Encoding.UTF8.GetBytes(data), false);
return Convert.ToBase64String(cipher );
}
Java decryption with private key(Successful)
public static void decrypt() throws Exception{
byte[] modulusBytes = Base64.getDecoder().decode(mod);
byte[] dByte = Base64.getDecoder().decode(d);
BigInteger modulus = new BigInteger(1, (modulusBytes));
BigInteger exponent = new BigInteger(1, (dByte));
RSAPrivateKeySpec rsaPrivKey = new RSAPrivateKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey privKey = fact.generatePrivate(rsaPrivKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] cipherData = Base64.getDecoder().decode(cipherByte);
byte[] plainBytes = cipher.doFinal(cipherData);
System.out.println(new String(plainBytes));
}
Problem is Here
When c# encrypt with private key and java decrypt with public key bad padding error occur:
C# encryption with private key(Fail)
public stringEncryptData(string data) {
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(xml); //private key
var cypher = rsa.Encrypt(Encoding.UTF8.GetBytes(data), false);
return Convert.ToBase64String(cypher);
}
java decryption with public key (Fail)
public static void decryptPublic() throws Exception{
byte[] modulusBytes = Base64.getDecoder().decode(mod);
byte[] expBytes = Base64.getDecoder().decode(exp);
BigInteger modulus = new BigInteger(1, (modulusBytes));
BigInteger exponent = new BigInteger(1, (expBytes));
RSAPublicKeySpec pubKey = new RSAPublicKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey publicKey = fact.generatePublic(pubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, publicKey );
byte[] cipherData = Base64.getDecoder().decode(cipherByte);
byte[] plainBytes = cipher.doFinal(cipherData);
System.out.println(new String(plainBytes));
}
I understand public key should use to do encryption and private key for decryption.But in my situation, i need to sent out public key to mutiple clients for decryption on a text encrypted by its private key. Text should be non readable by others except client.
Can anyone see what problem on my code, or suggest a better solution to my problem.
RSA encryption is only secure if a (secure) padding scheme is being used. RSA encryption schemes have been specified in PKCS#1 standards by RSA laboratories (now part of EMC2). These have been copied into RFC, such as RFC 3447: Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1.
For the purposes of this document, an encryption scheme consists of
an encryption operation and a decryption operation, where the
encryption operation produces a ciphertext from a message with a
recipient's RSA public key, and the decryption operation recovers the
message from the ciphertext with the recipient's corresponding RSA
private key.
So encryption with a private key is an undefined operation.
So what to do now:
securely distribute private keys instead of public keys
generate key pairs and securely transport the public key to the sender
if you require authentication/integrity instead of confidentiality, use signature generation instead of encryption
And, whatever you do, read into Public Key Infrastructure (PKI). It's a far stretching subject that you need to understand before you can apply it.
Encrypting with the private key/decrypting with the public key is a legitimate operation in RSA, however it is not used to protect data, it is instead used to authenticate the source of the data and its integrity. In this context the encryption operation is more usually called "signing".
Encrypting using the private key to protect data as you describe is insecure and so the fact that it is not easily done is likely intentional and intended to prevent incorrect use of the algorithm.
Distributing your private key to clients as suggested in the comments is also unwise since you have no control over who they may pass the key onto (accidentally or otherwise).
If you wish to encrypt data so that it can be decrypted by multiple distinct parties, then you should have each of them provide you with their own public key, and use that to encrypt the data separately for each client.
What i am making is a chat program that encrypts/decrypts messages send. So far i've established the basic all-to-all communication with clients and have put a server-to-client simple encryption with AES, basically i have given them the same key. Now i want to establish a key exchange algorithm that will allow every client to have its own key. I have read multiple algorithms, at first i wanted to establish a Diffie-Hellman but i found some RSA examples that made more sence. This is what i have:
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
Key aesKey = keyGenerator.generateKey();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
byte[] aesKeyBytes = aesKey.getEncoded();
System.out.println("1. aesKeyBytes= "+ bytesToHex(aesKeyBytes));
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] cipherText = cipher.doFinal(aesKeyBytes);
System.out.println("2. cipherText= "+bytesToHex(cipherText));
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
byte[] decryptedKeyBytes = cipher.doFinal(cipherText);
System.out.println("3. decryptedKeyBytes= "+bytesToHex(decryptedKeyBytes));
//use symmetric with the decrypted key
SecretKey newAesKey = new SecretKeySpec(decryptedKeyBytes, "AES");
Basically from the point this code ends i just use the SecretKey to initialize my AES cipher and go on from there. Now my question is how to distribute the keys from the RSA through sockets without losing bytes or whatever so that all clients can have unique keypairs with the server. Also is there a way to not use a keygenerator and give my own keys like Strings, since i plan on using setters so that the user can change his/her key on whish, otherwise what's the point in using keyexchange if all the clients end up having the same keys in the first place? And one last thing purely on curiosity, is it possible to modify this RSA to DH, and how are they different code-wise?
Lastly the "bytesToHex" method is as follows and was provided to me by my teacher and after tested it works fine so there are no problems there:
public static String bytesToHex(byte[] data)
{
if (data==null)
return null;
else
{
int len = data.length;
String str = "";
for (int i=0; i<len; i++)
{
if ((data[i]&0xFF)<16){
str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF);
}
else{
str = str + java.lang.Integer.toHexString(data[i]&0xFF);
}
}
return str.toUpperCase();
}
}
I know there might be answers like "look at this" and "this is a good example", but trust me i've looked at all of them i just got more confused. Just to be clear i don't want to use Files to store my keys or anything like that, i want simply the client and server to send each other their public keys so that along with their private key to create a secretKey, i know that this is basically DH but honestly i am so fed up right now that i'll take what i can get so anything you have to say is much appreciated.
I need crypto-algorithm AES in key wrap mode. Is there some open-source library or implementation that could be helpful?
It is important, that it must be key wrap mode.
The standard SunJCE provider offers an implementation of RFC 3394. Just use the algorithm AESWrap:
Cipher c = Cipher.getInstance("AESWrap", "SunJCE");
c.init(Cipher.WRAP_MODE, secretKey);
byte[] result = c.wrap(someKey);
BouncyCastle supports key wrapping in AES with the AESWrapEngine.
You can look at this StackOverflow post to see more examples of BouncyCastle. The only difference is you will specify the AESWrapEngine instead of the AESEngine.
yes there is a library available named Bouncy Castle you can use that library for wrapping your data encryption key using AES algorithm in WRAP_MODE ,here below code snippet might help you.
public static byte[] wrapKey(SecretKey key, SecretKey keyToWrap) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AESKW", "BCFIPS");
cipher.init(Cipher.WRAP_MODE, key);
return cipher.wrap(keyToWrap);
}
public static SecretKey unwrapKey(SecretKey key, byte[] wrappedKey) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AESKW", "BCFIPS");
cipher.init(Cipher.UNWRAP_MODE, key);
return (SecretKey) cipher.unwrap(wrappedKey, "AES", Cipher.SECRET_KEY);
}