Позвольте мне перефразировать часть вашего вопроса, чтобы сделать его более понятным, а также некоторые необходимые символы. То, как вы сформулировали свою схему, немного утомительно для понимания. Тем не менее, как @JamesKPolk и @MaartenBodewes указали, что для криптографии с эллиптической кривой, которая поддерживает шифрование, вам потребуется схема IES, называемая ECIES, которая может быть получена, например, как комбинация ECDH и симметричной схемы шифрования, такой как AES. Итак, давайте вернемся к схеме, которую вы пытались реализовать с Алисой и Бобом.
Bootstrap
- Алиса и Боб генерируют своисоответствующие ключи AES, которые состоят из
SecretKey
и IV
. В этом примере мы будем использовать AES256
- Алиса и Боб генерируют свои соответствующие пары ключей EC и каким-то образом делятся своими открытыми ключами, чтобы каждый знал другой открытый ключ.
- У Алисы есть открытый ключ Боба.
- У Боба есть открытый ключ Алисы.
Требуемая схема
- Алиса шифрует текстовое сообщение
<em>m</em>
с помощью AES для генерации зашифрованного сообщения e m . - Прежде чем это произойдет, Алиса генерирует ключ AES, который содержит
SecretKey
, используемый для шифрования, и вектор IV
. В следующем примере кода мы будем называть этот кортеж как AESPair
.
- Алиса шифрует сообщение
SecretKey (SK) || IV
открытым ключом Бобса, используя ECIES для получения е ск || IV . - Алиса генерирует
SharedSecret
, используя закрытый ключ Алисы и открытый ключ Боба. Давайте назовем это SSK 1 - Боб может сгенерировать
SharedSecet
, используя личный ключ Боба и открытый ключ Алисы. Давайте назовем это SSK 2 - В этот момент SSK 1 == SSK 2 . Вы можете найти причину этого здесь в разделе IES Расшифровка .
- Алиса отправляет зашифрованный текст e m и зашифрованный секретный ключ и параметры IV, необходимые ( e sk || iv ) Бобу.
- Боб расшифровывает зашифрованное сообщение, содержащее секрет AES и IV ( e sk || iv ) со своим закрытым ключом для получения (SK || IV)
- Боб расшифровывает зашифрованный текст e m с полученным секретным ключом на шаге 4 для получения исходного сообщения, отправленного Алисой, т.е. m
- Готово
Код
Вспомогательные функции
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
Объяснение кода контрольного примера
- Ключ AES256 и IV генерируются для
Alice
- Простое текстовое сообщение
"plain text from alice to bob"
шифруется Алисой с использованием ключа, сгенерированного на шаге 1. - Создается новый байтовый массив с конкатенацией
key || IV
ключа Алисы. Это сообщение должно быть зашифровано и отправлено Бобу через ECIES. - Алиса и Боб генерируют свои пары ключей эллиптической кривой, и мы предполагаем, что они знают открытые ключи друг друга. Генерация ключа происходит по методу
ECC.generateKeyPair()
. - Алиса использует открытый ключ Боба и закрытый ключ Алисы для генерации
Shared Secret
, который является симметричным SecretKey
- Алиса шифрует сообщение вшаг 3 с использованием общего секретного ключа на шаге 5, который создает зашифрованное сообщение, которое необходимо отправить Бобу.
- Боб получает сообщения (Шаг 6 и Шаг 2) и вычисляет общий секретный ключ с использованием личного ключа Бобаи открытый ключ Алисы.
- Боб использует ключ, созданный на шаге 7, для расшифровки зашифрованного сообщения, полученного от Алисы.
- Теперь Боб знает ключ AES, который Алиса использовала на шаге 2 для шифрования исходного текстового сообщения. Сообщение, полученное после расшифровки, представляет собой
byte[]
, который преобразуется в объект AESPair
, создающий необходимые SecretKey
и IV
. - Боб дешифрует зашифрованное сообщение на шаге 2 и восстанавливает исходное сообщение
"plain text from alice to bob"
Надеюсь, это поможет. Дайте мне знать, если вам нужны пояснения.