【问题标题】:Using ECC Curve25519 to encrypt/decrypt data in Java使用 ECC Curve25519 在 Java 中加密/解密数据
【发布时间】:2016-02-08 14:34:49
【问题描述】:

我正在尝试在我的 Android 应用程序中使用 Curve25519 在本地加密/解密 AES 加密密钥。我不需要任何密钥交换、密钥协议或签名。为什么我需要使用那条特定的曲线?因为我需要能够自己提供私钥并能够计算出它的匹配公钥。据我所知,只有 Curve25519 可以做到这一点。如果我错了,请纠正我。

所有 Curve25519 实现只进行密钥生成、密钥交换和签名/验证。

在我获得 Curve25519 私钥/公钥后是否可以进行数据加密/解密,或者您可以建议任何符合我标准的替代曲线?

编辑

那我为什么需要这个?我会更仔细地解释。我的应用程序正在执行本地文件加密,尤其是照片。我的目标是能够让用户在不输入密码的情况下拍照,然后输入密码来查看它们。 为此,我需要能够从密码创建公钥/私钥对,并能够在提供相同密码时即时重新创建相同的密钥对。因此,在第一次运行时,我从密码生成 ECC 密钥对并将公钥存储在设备上。当用户想要拍摄一张新照片时,应用程序会使用随机 256 位 AES 密钥加密照片,然后使用存储的公钥加密该密钥。当用户想要查看照片时,他/她提供正确的密码,我导出相同的 ECC 密钥对并用我的私钥解密 AES 密钥,然后我可以使用 AES 256 解密照片。

所以就我而言,Curve25519 可以给我这种能力,或者还有其他选择。欢迎使用 Java 代码示例!

【问题讨论】:

  • 您可能可以使用this 来实例化ECIES。
  • 与接收方公钥进行密钥交换,并使用共享密钥进行对称加密,例如 XSalsa20Poly1305 或 AES-GCM。
  • 可能你没听懂。我没有任何接收方,所有的加解密都是本地的。
  • @AlexAmiryan 你仍然有两个聚会,即使他们在同一个系统上。用户是接收者,而加密文件的对象是发送者。如果只有一方,使用非对称加密将毫无意义。
  • @AlexAmiryan 您可以使用一次性发件人密钥在 NaCl 的盒子中。这与 ECIES 几乎相同。 NaCl 的加密有多种实现,包括 NaCl 本身、TweetNaCl、LibSodium 或我的 C# 端口。只需创建一个新密钥,与接收者计算共享密钥,从共享密钥(使用 KDF 或哈希)派生您的 AES 密钥。在密文中包含发送者的公钥,以便接收者可以计算出相同的共享密钥。

标签: java android cryptography public-key-encryption elliptic-curve


【解决方案1】:

他们在 Android 设备上加密文件的关键是永远不要存储密钥。为此,您添加了加密图片不需要密码的约束。最后,由于非对称加密速度很慢,因此您需要 AES 来完成繁重的工作。

这适用于 RSA 或任何其他非对称算法。

初始运行

  1. 首次运行时,生成密钥对并要求用户输入密码。
  2. 以明文形式存储公钥。使用从密码生成的 AES 密钥加密私钥。

加密

  1. 为每张图片生成不同的 AES 密钥并用它加密图像。使用公钥加密 AES 密钥并将其与图片一起存储。

解密

  1. 让用户输入密码。从中生成第一个 AES 密钥。用它来解密私钥。使用私钥解密此图像的 AES 密钥。使用 AES 密钥解密文件并显示它。

(我们实际上有 4 个密钥。我们需要密钥对来允许我们在没有密码的情况下进行加密。我们需要第一个 AES 密钥来安全地存储私钥。我们需要第二个更改的 AES 密钥来加密文件。 )

我认为这应该是安全的,除了键记录等攻击。 Java 代码应该非常简单。希望这很清楚。

================================================ ========================

使用 AES 和 RSA 的完整示例。对于 Curve,仅切换 RSA 代码,尽管它不支持开箱即用,因此您需要一个外部库。此外,为了时间和简洁性,代码的安全性低于应有的安全性。例如,我使用的是 ECB 而不是 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;
    }
}

}

【讨论】:

  • 您所描述的内容事先很清楚,我会这样做。我需要的是代码 sn-p,只是使用 Curve25519 ECC 生成密钥和数据加密/解密的示例。
  • 有什么理由必须是曲线?它不是标准 Android 的一部分,而 AES256 和 RSA 是。
猜你喜欢
  • 2021-04-18
  • 1970-01-01
  • 2010-11-12
  • 2018-03-13
  • 2013-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多