6

有爱丽丝和鲍勃。我想实现以下过程:

  1. Alice 使用 AES 加密文本并生成密钥
  2. Alice 使用带有 El Gamal 的 Elliptic Curves 使用 Bobs 公钥加密此密钥
  3. Alice 将加密文本和加密密钥发送给 Bob
  4. Bob 用他的私钥解密密钥
  5. Bob 用解密后的密钥解密文本
  6. 完毕

我正在使用 bouncycastle 的 ECElGamalEncryptor 类。我的问题是,据我了解,此类使用公钥加密椭圆曲线上的一个点,但我的 AES 密钥不是 ECPoint,而是十六进制。

假设我有这个用于 AES 加密的 128 位密钥:

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 密钥,我不知道我现在要做什么。

4

1 回答 1

5

让我尝试改写您问题的一部分,使其更加清晰,并附上一些必要的符号。你措辞的方式有点难以理解。但是,正如@JamesKPolk 和@MaartenBodewes 指出的那样,支持加密的椭圆曲线密码学需要一个称为ECIES 的IES 方案,它可以作为ECDH 和AES 等对称加密方案的组合获得。因此,让我们重新审视一下您尝试与 Alice 和 Bob 一起实施的方案。

引导程序

  • Alice 和 Bob 生成各自的 AES 密钥,由 aSecretKey和a 组成IV。在这个例子中,我们将使用AES256
  • Alice 和 Bob 生成他们相应的 EC 密钥对,并以某种方式共享他们的公钥,以便每个人都知道另一个公钥。
    • Alice 拥有 Bob 的公钥。
    • Bob 拥有 Alice 的公钥。

所需方案

  1. Alice 使用 AES 加密纯文本消息m以生成加密消息e m
    • 在此之前,Alice 会生成一个 AES 密钥,其中包含SecretKey用于加密的密钥和一个IV向量。在接下来的代码示例中,我们将此元组称为AESPair.
  2. Alice 使用 ECIES 使用 Bobs 公钥加密消息SecretKey (SK) || IV以获得sk||iv
    • AliceSharedSecret使用 Alice 的私钥和 Bob 的公钥生成。我们称之为 SSK 1
    • Bob 可以SharedSecet使用 Bob 的私钥和 Alice 的公钥生成。我们称之为 SSK 2
    • 此时 SSK 1 ==SSK 2您可以在 IES 的解密部分中找到原因。
  3. Alice 将加密文本e m和加密的密钥以及必要的 IV 参数 ( e sk||iv ) 发送给 Bob。
  4. Bob用他的私钥解密包含AES秘密和IV(e sk||iv )的加密消息,得到(SK || IV)
  5. Bob用第 4 步得到的秘钥对密文e m进行解密,得到 Alice 发送的原始消息 ie 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");
  }

}

AESAir.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. Alice 使用步骤 1 中生成的密钥对纯文本消息"plain text from alice to bob"进行加密。
  3. 一个新的字节数组是由key || IVAlice 的密钥的串联创建的。这是应该加密并通过 ECIES 发送给 Bob 的消息。
  4. Alice 和 Bob 生成他们的椭圆曲线密钥对,我们假设他们知道彼此的公钥。密钥生成发生在ECC.generateKeyPair()方法中。
  5. Alice 使用 Bob 的公钥和 Alice 的私钥生成Shared Secret一个对称的SecretKey
  6. Alice 在第 3 步中使用第 5 步中的共享密钥对消息进行加密,这会创建需要发送给 Bob 的加密消息。
  7. Bob 接收消息(步骤 6 和步骤 2)并使用 Bob 的私钥和 Alice 的公钥计算共享密钥。
  8. Bob 使用在步骤 7 中构造的密钥来解密从 Alice 收到的加密消息。
  9. 现在 Bob 知道 Alice 在步骤 2 中用于加密原始纯文本消息的 AES 密钥。解密后得到的消息是a byte[],它被转换为一个AESPair创建SecretKeyIV必要的对象。
  10. Bob 在步骤 2 中解密加密消息并恢复原始消息"plain text from alice to bob"

希望这可以帮助。如果您需要澄清,请告诉我。

于 2019-10-11T00:57:24.860 回答