I am trying to use Curve25519 in my Android app to encrypt/decrypt AES encryption key locally. I don't need any key exchange, key agreement or signing. Why I need to use that particular curve? Because I need to be able to provide private key myself and be able to calculate it's matching public key. So as far as I got, only Curve25519 does this. Please correct me if I am wrong.
All Curve25519 implementations just do key generation, key exchange and signing/verifying.
Is it possible to do data encryption/decryption after I get Curve25519 private/public keys or maybe you can suggest any alternative curves that matches my criteria?
Edit
So why I need this? I'll explain more carefully. My app is doing local file encryption, particularly photos. My goal is to be able for user to take a photo without entering a password and then enter password to view them. For this I need to be able to create public/private keypair from password and be able to recreate on the fly the same keypair whenever same password is provided. So on first run I generate ECC keypair from password and store public key on the device. When user wants to take a new photo app encrypts photo with random 256 bit AES key and then encrypt that key with stored public key. When user wants to view photo, he/she provides correct password, I derive same ECC keypair and decrypt AES key with my private key and then I can decrypt the photo using AES 256.
So as far as I get Curve25519 can give me this ability or there are any other alternatives. Code examples in Java are welcomed!
The elliptic curve cryptography (ECC) does not directly provide encryption method. Instead, we can design a hybrid encryption scheme by using the ECDH (Elliptic Curve Diffie–Hellman) key exchange scheme to derive a shared secret key for symmetric data encryption and decryption.
They key to encrypting files on an Android device is to never store the key. To this you've added the constraint that encrypting pictures should not require a password. Finally, because asymmetric encryption is slow, you would need AES to do the heavy lifting.
This works with RSA or any other asymmetric algorithm.
Initial Run
Encryption
Decryption
(We actually have 4 keys. We need the key pair to allow us to encrypt without a password. We need the first AES key to store the private key securely. We need the second and changing AES key to encrypt the file.)
I think this should be secure, except for attacks such as key logging. The Java code should be very straightforward. Hope this is clear.
======================================================================
Complete example Using AES and RSA. For Curve, switch the RSA code only, though it isn't supported out of the box, so you'll need an external library. Also, in the interest of time and brevity, the code is less secure than it should be. For example, I used ECB and not CBC.
package il.co.falk; import javax.crypto.*; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; import java.security.*; import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; public class SecureFile { private PublicKey publicKey; private byte[] privateKeyArray; private byte[] salt = {1,2,3,4,5,6,7,8}; public static void main(String[] args) { String password = "PASSWORD"; SecureFile secureFile = new SecureFile(password); secureFile.test(); } public void test() { String password = "PASSWORD"; String imageFile = "348756348975634897562398479623896"; ImageAndKey imageAndKey = encryptImage(imageFile.getBytes()); byte[] decryptedImage = decryptImage(imageAndKey, password); System.out.println(new String(imageFile)); System.out.println(new String(decryptedImage)); } public SecureFile(String password) { try { generateRSAKeys(password); } catch (Exception e) { e.printStackTrace(); } } public ImageAndKey encryptImage(byte[] imageBytes) { try { byte[] secretKeyBytes = generateAESKey(); byte[] encryptedFile = aesEncrypt(imageBytes, secretKeyBytes); byte[] encryptedKey = rsaEncrypt(secretKeyBytes); return new ImageAndKey(encryptedFile, encryptedKey); } catch (Exception e) { e.printStackTrace(); return null; } } public byte[] decryptImage(ImageAndKey imageAndKey, String password) { try { byte[] secretKeyBytes = generateAESKey(password); byte[] decryptedPrivateKey = aesDecrypt(privateKeyArray, secretKeyBytes); byte[] decryptedKey = rsaDecrypt(imageAndKey.aesKey, decryptedPrivateKey); SecretKey secretKey = new SecretKeySpec(decryptedKey, "AES"); secretKeyBytes = secretKey.getEncoded(); byte[] decryptedBytes = aesDecrypt(imageAndKey.imageBytes, secretKeyBytes); return decryptedBytes; } catch (Exception e) { e.printStackTrace(); } return null; } // RSA private void generateRSAKeys(String password) throws Exception { final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(512); // TODO: make this 2048 at least final KeyPair keyPair = keyGen.generateKeyPair(); publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); byte[] secretKeyBytes = generateAESKey(password); byte[] privateKeyBytes = privateKey.getEncoded(); privateKeyArray = aesEncrypt(privateKeyBytes, secretKeyBytes); } public byte[] rsaEncrypt(byte[] plainText) throws Exception { final Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] cipherText = cipher.doFinal(plainText); return cipherText; } public byte[] rsaDecrypt(byte[] cipherText, byte[] decryptedPrivateKeyArray) throws Exception { PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decryptedPrivateKeyArray)); final Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] plainText = cipher.doFinal(cipherText); return plainText; } // AES private byte[] aesEncrypt(byte[] plainText, byte[] secretKeyBytes) throws Exception { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(secretKeyBytes)); byte[] cipherText = cipher.doFinal(plainText); return cipherText; } public byte[] aesDecrypt(byte[] cipherText, byte[] secretKeyBytes) throws Exception { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, getSecretKey(secretKeyBytes)); byte[] plainText = cipher.doFinal(cipherText); return plainText; } private byte[] generateAESKey() throws Exception { KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(256); SecretKey secretKey = keyGen.generateKey(); return secretKey.getEncoded(); } private byte[] generateAESKey(String password) throws Exception { SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256); SecretKey tmp = factory.generateSecret(spec); SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES"); return secret.getEncoded(); } private SecretKey getSecretKey(byte[] secretKeyBytes) throws Exception { SecretKey secretKey = new SecretKeySpec(secretKeyBytes, "AES"); return secretKey; } // Classes class ImageAndKey { public byte[] imageBytes; public byte[] aesKey; public ImageAndKey(byte[] imageBytes, byte[] aesKey) { this.imageBytes = imageBytes; this.aesKey = aesKey; } }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With