Зашифруйте секретный ключ AES с помощью эллиптической кривой Эль-Гамаля

Есть Алиса и Боб. Я хочу реализовать следующий процесс:

  1. Алиса шифрует текст с помощью AES и генерирует секретный ключ
  2. Алиса шифрует этот секретный ключ открытым ключом Бобса, используя Эллиптические кривые с Эль-Гамалем.
  3. Алиса отправляет Бобу зашифрованный текст и зашифрованный секретный ключ.
  4. Боб расшифровывает секретный ключ своим личным ключом.
  5. Боб расшифровывает текст расшифрованным секретным ключом.
  6. Выполнено

Я использую класс ECElGamalEncryptor из bouncycastle. Моя проблема в том, что, насколько я понимаю, этот класс шифрует точку на эллиптической кривой с использованием открытого ключа, но мой секретный ключ AES не ECPoint, а шестнадцатеричный.

Представим, что у меня есть 128-битный секретный ключ для шифрования AES:

6D5A7134743777397A24432646294A40

И это то, что у меня есть на данный момент:

import java.math.BigInteger;
import java.security.SecureRandom;

import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.sec.SECNamedCurves;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.ec.ECElGamalDecryptor;
import org.bouncycastle.crypto.ec.ECElGamalEncryptor;
import org.bouncycastle.crypto.ec.ECPair;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.math.ec.ECPoint;
class TestClass {

  public static void main(String[] argv) {

    // Get domain parameters for example curve secp256r1
    X9ECParameters ecp = SECNamedCurves.getByName("secp256r1");
    ECDomainParameters domainParams = new ECDomainParameters(ecp.getCurve(),
                                                             ecp.getG(), ecp.getN(), ecp.getH(),
                                                             ecp.getSeed());

    // Generate a private key and a public key
    AsymmetricCipherKeyPair keyPair;
    ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, new SecureRandom());
    ECKeyPairGenerator generator = new ECKeyPairGenerator();
    generator.init(keyGenParams);
    keyPair = generator.generateKeyPair();

    ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate();
    ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic();
    byte[] privateKeyBytes = privateKey.getD().toByteArray();


    // Get ECPoint Q from privateKey
    ECPoint Q = domainParams.getG().multiply(new BigInteger(privateKeyBytes));  

    //Initialize ECElGamalEncryptor
    ECElGamalEncryptor elgamalEn = new ECElGamalEncryptor();
    elgamalEn.init(publicKey);
    ECPair encrypted = elgamalEn.encrypt(Q);

    //Encryption
    ECElGamalDecryptor elgamalDe = new ECElGamalDecryptor();
    elgamalDe.init(privateKey);
    ECPoint original = elgamalDe.decrypt(encrypted);

  }
}

Итак, я могу инициализировать ECElGamalEncryptor и зашифровать ECPoint Q с помощью открытого ключа. Но на самом деле я хочу зашифровать секретный ключ AES и понятия не имею, что мне теперь делать.


person aslanj    schedule 03.10.2019    source источник
comment
Вам нужна схема IES под названием ECIES. Я думаю, что она есть в Баунсикасле.   -  person President James K. Polk    schedule 03.10.2019
comment
И как я могу использовать ECIES для шифрования с помощью ECElGamal?   -  person aslanj    schedule 04.10.2019
comment
Вот описательное сравнение между ними: crypto.stackexchange.com/a/70648/1172. Если вы можете зашифровать значения, вы, как правило, также можете зашифровать секретные ключи и зашифровать данные с помощью этого ключа. Обычно лучше использовать ECIES (ECDH + симметричное шифрование).   -  person Maarten Bodewes    schedule 06.10.2019
comment
Привет, aslanj, То, что сказано в предыдущих комментариях, по существу. Я ввел ответ, который может помочь вам с примером реализации.   -  person Sudheesh Singanamalla    schedule 12.10.2019


Ответы (1)


Позвольте мне попытаться перефразировать часть вашего вопроса, чтобы сделать его более ясным и добавить некоторые необходимые символы. То, как вы сформулировали свою схему, немного утомительно для понимания. Однако, как указали @JamesKPolk и @MaartenBodewes, для криптографии Elliptic Curve, которая поддерживает шифрование, вам понадобится схема IES под названием ECIES, которую можно получить как комбинацию ECDH и схемы симметричного шифрования, например, AES. Итак, давайте вернемся к схеме, которую вы пытались реализовать с Алисой и Бобом.

Bootstrap

  • Алиса и Боб генерируют свои соответствующие ключи AES, которые состоят из SecretKey и IV. В этом примере мы будем использовать AES256
  • Alice and Bob generate their corresponding EC KeyPairs and in some way share their PublicKeys so that each one has the knowledge of the other public key.
    • Alice has Bob's public key.
    • У Боба есть открытый ключ Алисы.

Желаемая схема

  1. Alice encrypts a plain text message m with AES to generate encrypted message em.
    • Before this can happen Alice generates an AES Key which contains the SecretKey used for encryption and an IV vector. In the code example that'll follow we'll call this tuple as an AESPair.
  2. Alice encrypts the SecretKey (SK) || IV message with Bobs public key using ECIES to obtain esk||iv.
    • Alice generates the SharedSecret using Alice's Private Key and Bob's Public Key. Let's call this SSK1
    • Боб может сгенерировать SharedSecet, используя закрытый ключ Боба и открытый ключ Алисы. Назовем этот SSK 2.
    • На данный момент SSK 1 == SSK 2. Причину этого можно найти здесь, в разделе Расшифровка IES.
  3. Алиса отправляет зашифрованный текст e m и необходимые зашифрованный секретный ключ и параметры IV (e sk || iv) Бобу.
  4. Боб расшифровывает зашифрованное сообщение, содержащее секрет AES и IV (e sk || iv) своим закрытым ключом, чтобы получить (SK || IV)
  5. Боб расшифровывает зашифрованный текст e m с помощью секретного ключа, полученного на шаге 4, чтобы получить исходное сообщение, отправленное Алисой, т.е. m
  6. Выполнено

Код

Вспомогательные функции

private final static char[] hexArray = "0123456789ABCDEF".toCharArray();

public static String convertBytesToHex(byte[] bytes) {
  char[] hexChars = new char[bytes.length * 2];
  for ( int j = 0; j < bytes.length; j++ ) {
    int v = bytes[j] & 0xFF;
    hexChars[j * 2] = hexArray[v >>> 4];
    hexChars[j * 2 + 1] = hexArray[v & 0x0F];
  }
  return new String(hexChars).toLowerCase();
}

public static byte[] hexStringToByteArray(String hexString){
  byte[] bytes = new byte[hexString.length() / 2];

  for(int i = 0; i < hexString.length(); i += 2){
    String sub = hexString.substring(i, i + 2);
    Integer intVal = Integer.parseInt(sub, 16);
    bytes[i / 2] = intVal.byteValue();
    String hex = "".format("0x%x", bytes[i / 2]);
  }
  return bytes;
}

ECC.java

public class ECC {
  // Both Alice and Bob agree upon this value in some manner before starting this protocol.
  public static byte[] iv = new SecureRandom().generateSeed(16);

  static {
    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
  }

  public static KeyPair generateKeyPair() throws InvalidAlgorithmParameterException, NoSuchProviderException, NoSuchAlgorithmException {
      ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
      KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDH", "BC");

      keyPairGenerator.initialize(parameterSpec);

    return keyPairGenerator.generateKeyPair();
  }

  public static SecretKey generateSharedSecret(PrivateKey privateKey, PublicKey publicKey) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException {
      KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH", "BC");
      keyAgreement.init(privateKey);
      keyAgreement.doPhase(publicKey, true);

    return keyAgreement.generateSecret("AES");
  }

  public static byte[] encrypt(SecretKey key, byte[] plainTextBytes) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, InvalidKeyException, ShortBufferException, BadPaddingException, IllegalBlockSizeException {
      IvParameterSpec ivSpec = new IvParameterSpec(iv);
      Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
      byte[] cipherText;

      cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
      cipherText = new byte[cipher.getOutputSize(plainTextBytes.length)];
      int encryptLength = cipher.update(plainTextBytes, 0, plainTextBytes.length, cipherText, 0);
      encryptLength += cipher.doFinal(cipherText, encryptLength);

      return cipherText;
  }

  public static byte[] decrypt(SecretKey key, byte[] cipherTextBytes) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, InvalidKeyException, ShortBufferException, BadPaddingException, IllegalBlockSizeException {
      Key decryptionKey = new SecretKeySpec(key.getEncoded(),
          key.getAlgorithm());
      IvParameterSpec ivSpec = new IvParameterSpec(iv);
      Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
      byte[] plainText;

      cipher.init(Cipher.DECRYPT_MODE, decryptionKey, ivSpec);
      plainText = new byte[cipher.getOutputSize(cipherTextBytes.length)];
      int decryptLength = cipher.update(cipherTextBytes, 0, cipherTextBytes.length, plainText, 0);
      decryptLength += cipher.doFinal(plainText, decryptLength);

      return plainText;
  }
}

AES256.java

public class AES256 {

  public static AESPair generateKeyPair() throws NoSuchAlgorithmException {
    KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
    keyGenerator.init(256);

    SecretKey key = keyGenerator.generateKey();
    byte[] IV = new byte[16];
    SecureRandom random = new SecureRandom();
    random.nextBytes(IV);

    AESPair response = new AESPair(key, IV);
    return response;
  }

  public static byte[] encrypt(byte[] plainText, SecretKey key, byte[] IV) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
    IvParameterSpec ivParameterSpec = new IvParameterSpec(IV);
    cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParameterSpec);
    byte[] cipherText = cipher.doFinal(plainText);
    return cipherText;
  }

  public static byte[] decrypt(byte[] cipherText, SecretKey key, byte[] IV) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
    IvParameterSpec ivSpec = new IvParameterSpec(IV);
    cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
    byte[] decryptedText = cipher.doFinal(cipherText);
    return decryptedText;
  }

  public static byte[] serializeSecretKey (SecretKey key) {
    return key.getEncoded();
  }

  public static SecretKey deserializeSecretKey (byte[] sk) {
    return new SecretKeySpec(sk, 0, sk.length, "AES");
  }

}

AESPair.java, который является соответствующим помощником для AES.

public class AESPair {
  private SecretKey key;
  private byte[] IV;

  public void setIV(byte[] IV) {
    this.IV = IV;
  }

  public void setKey(SecretKey key) {
    this.key = key;
  }

  public byte[] getIV() {
    return IV;
  }

  public SecretKey getKey() {
    return key;
  }

  public AESPair(SecretKey sk, byte[] ivBytes) {
    key = sk;
    IV = ivBytes;
  }

  // This takes in SK || IV for AES256 and creates the SecretKey object and corresponding IV byte array.
  public AESPair(byte[] skConcatIVBytes) {
    int total_bytes = skConcatIVBytes.length;
    // FOR AES256 the key is 32 bytes and the IV is 16 bytes
    byte[] sk = Arrays.copyOfRange(skConcatIVBytes, 0, 32);
    byte[] iv = Arrays.copyOfRange(skConcatIVBytes, 32, total_bytes);

    key = new SecretKeySpec(sk, 0, sk.length, Constant.AES);
    IV = iv;
  }
}

Теперь, когда у нас есть все, что нам нужно, давайте соберем желаемую схему в качестве теста.

@Test
public void test_scheme_ecc() throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchProviderException, ShortBufferException {
    String plainText = "plaintext message from alice to bob";
    System.out.println("Original plaintext message: " + plainText);

    AESPair aliceAESPair = AES256.generateKeyPair();
    AESPair bobAESPair = AES256.generateKeyPair();

    byte[] encryptedPlainTextMessageFromAlice = AES256.encrypt(plainText.getBytes(StandardCharsets.UTF_8), aliceAESPair.getKey(), aliceAESPair.getIV());
    System.out.println("Alice encrypted message : " + convertBytesToHex(encryptedPlainTextMessageFromAlice));

    // Necessary Key + IV information to reconstruct the key
    byte[] keyInformation = ByteBuffer.allocate(aliceAESPair.getKey().getEncoded().length + aliceAESPair.getIV().length)
        .put(aliceAESPair.getKey().getEncoded())
        .put(aliceAESPair.getIV())
        .array();
    System.out.println("Alice's SK || IV : " + convertBytesToHex(keyInformation));

    // Initialize two key pairs
    KeyPair aliceECKeyPair = ECC.generateKeyPair();
    KeyPair bobECKeyPair = ECC.generateKeyPair();

    System.out.println("Alice EC PK : " + convertBytesToHex(aliceECKeyPair.getPublic().getEncoded()));
    System.out.println("Bob EC PK   : " + convertBytesToHex(bobECKeyPair.getPublic().getEncoded()));

    // Create two AES secret keys to encrypt/decrypt the message
    SecretKey aliceSharedSecret = ECC.generateSharedSecret(aliceECKeyPair.getPrivate(), bobECKeyPair.getPublic());
    System.out.println("Alice Shared Secret Key : " + convertBytesToHex(aliceSharedSecret.getEncoded()));

    // Encrypt the message using 'aliceSharedSecret'
    byte[] cipherText = ECC.encrypt(aliceSharedSecret, keyInformation);
    System.out.println("Encrypted cipher text: " + convertBytesToHex(cipherText));

    // Decrypt the message using 'bobSharedSecret'
    SecretKey bobSharedSecret = ECC.generateSharedSecret(bobECKeyPair.getPrivate(), aliceECKeyPair.getPublic());
    System.out.println("Bob Shared Secret Key : " + convertBytesToHex(bobSharedSecret.getEncoded()));

    byte[] decrypted_EncryptedTextFromAlice = ECC.decrypt(bobSharedSecret, cipherText);
    System.out.println("Decrypted cipher text to obtain Alice generated secret key: " + convertBytesToHex(decrypted_EncryptedTextFromAlice));

    AESPair reconstructedKey = new AESPair(decrypted_EncryptedTextFromAlice);

    byte[] decryptedText = AES256.decrypt(encryptedPlainTextMessageFromAlice, reconstructedKey.getKey(), reconstructedKey.getIV());
    System.out.println("Decrypted plain text message : " + new String(decryptedText));
}

Вот результат теста:

Original plaintext message: plaintext message from alice to bob
Alice encrypted message : 9d273ea89ab6b8d170941d2578f0d4e11b1d6a3be199189dbbf4a5ff64fbf1348edbb459e38dac17aad6a68b1a95300f
Alice's SK || IV : 857248ab0171a652926fcc46353831965dd2d98cb4920de7d629c07250bc60fb60306f67d2c44e725b2e8344d970b34b
Alice EC PK : 3059301306072a8648ce3d020106082a8648ce3d030107034200042499c59fea8ab010782444825c7872c04407a4f034d907ca9014b9f8d4be1226cb9fc9eff57f8e0e7b8e1aa83290c6d6c3a56aeeef3490e1e55476e94abb4128
Bob EC PK   : 3059301306072a8648ce3d020106082a8648ce3d03010703420004d91562882f30b54177449941b9812b17ac5a59d2b80cc5fbaef833426152623dfb17965ba9897edd5da26b4044071882f8ae53ce37c24f0ea5b55b7e42b689ac
Alice Shared Secret Key : 3fa7b4ae68ff51296293b69ac1b0d8d139bf3f6a60732a124734a19f2987b772
Encrypted cipher text: 758506913bee96816f7a3190720ce7f01ddb8acbeaef1e669af420c04036a4b2ab446ce2a2bee62f603a0400b9076c927f2eeffc2a4cec0ffad756fed19dc6d9
Bob Shared Secret Key : 3fa7b4ae68ff51296293b69ac1b0d8d139bf3f6a60732a124734a19f2987b772
Decrypted cipher text to obtain Alice generated secret key: 857248ab0171a652926fcc46353831965dd2d98cb4920de7d629c07250bc60fb60306f67d2c44e725b2e8344d970b34b
Decrypted plain text message : plaintext message from alice to bob
BUILD SUCCESSFUL in 1s

Объяснение кода тестового примера

  1. Ключ AES256 и IV создается для Alice
  2. Простое текстовое сообщение "plain text from alice to bob" шифруется Алисой с помощью ключа, сгенерированного на шаге 1.
  3. Новый байтовый массив создается путем конкатенации key || IV ключа Алисы. Это сообщение следует зашифровать и отправить Бобу через ECIES.
  4. Алиса и Боб генерируют свои пары ключей эллиптических кривых, и мы предполагаем, что они знают открытые ключи друг друга. Генерация ключа происходит в ECC.generateKeyPair() методе.
  5. Алиса использует открытый ключ Боба и закрытый ключ Алисы для создания Shared Secret, который является симметричным SecretKey
  6. Алиса шифрует сообщение на шаге 3, используя общий секретный ключ на шаге 5, который создает зашифрованное сообщение, которое необходимо отправить Бобу.
  7. Боб получает сообщения (этап 6 и этап 2) и вычисляет общий секретный ключ, используя закрытый ключ Боба и открытый ключ Алисы.
  8. Боб использует ключ, созданный на шаге 7, для дешифрования зашифрованного сообщения, полученного от Алисы.
  9. Теперь Боб знает ключ AES, который Алиса использовала на шаге 2 для шифрования исходного текстового сообщения. Сообщение, полученное после дешифрования, представляет собой byte[], который преобразуется в объект AESPair, создавая необходимые SecretKey и IV.
  10. Боб расшифровывает зашифрованное сообщение на шаге 2 и восстанавливает исходное сообщение "plain text from alice to bob"

Надеюсь это поможет. Дайте мне знать, если вам нужны пояснения.

person Sudheesh Singanamalla    schedule 11.10.2019
comment
Обратите внимание, что вопрос явно задан для реализации с использованием Эль-Гамаля. Похоже, этот ответ рассматривает вопрос как XY-проблему, то есть действительно спрашивает, как зашифровать с использованием криптографии Elliptic Curve. Однако, на мой вкус, вопрос слишком конкретен для этого, поэтому я не вижу в этом решения. В комментарии я показал, что есть хотя бы теоретический способ решить эту проблему с помощью EC ElGamal. - person Maarten Bodewes; 12.10.2019