#java #encryption #cryptography #bouncycastle #encryption-symmetric
Вопрос:
Примечания к выпуску для выпуска Bouncycastle: 1.69 (7 июня 2021 года) состояние:
Реализация двух алгоритмов FPE, FF1 и FF3-1 в SP 800-38G, была добавлена в облегченный API и поставщик JCE.
Их можно найти в bcprov-jdk15on
банке.
Вот код, который пытается его использовать:
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.crypto.AlphabetMapper;
import org.bouncycastle.crypto.util.BasicAlphabetMapper;
import org.bouncycastle.jcajce.spec.FPEParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.testng.annotations.Test;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
@Slf4j
public class AesFpe {
@Test
public void testAesFpe() throws Exception {
SecretKey key = generateKey();
byte[] tweak = getTweak();
int radix = getRadix("0123456789");
Charset encoding = StandardCharsets.UTF_8;
byte[] plaintext = "510123456".getBytes(encoding);
Cipher cipher = Cipher.getInstance("AES/FF3-1/NoPadding", new BouncyCastleProvider());
byte[] ciphertext = encrypt(cipher, key, tweak, radix, plaintext);
log.info("Ciphertext: {}", new String(ciphertext));
byte[] decrypted = decrypt(cipher, key, tweak, radix, ciphertext);
assertThat(decrypted, equalTo(plaintext));
}
public byte[] encrypt(Cipher cipher, SecretKey key, byte[] tweak, int radix, byte[] plaintext) throws Exception {
AlgorithmParameterSpec fpeParameterSpec = new FPEParameterSpec(radix, tweak);
cipher.init(Cipher.ENCRYPT_MODE, key, fpeParameterSpec);
return cipher.doFinal(plaintext);
}
public byte[] decrypt(Cipher cipher, SecretKey key, byte[] tweak, int radix, byte[] ciphertext) throws Exception {
AlgorithmParameterSpec fpeParameterSpec = new FPEParameterSpec(radix, tweak);
cipher.init(Cipher.DECRYPT_MODE, key, fpeParameterSpec);
return cipher.doFinal(ciphertext);
}
private SecretKey generateKey() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
int keyLength = 256;
keyGenerator.init(keyLength);
return keyGenerator.generateKey();
}
private byte[] getTweak() {
int tweakLength = 7;
byte[] tweak = new byte[tweakLength];
new SecureRandom().nextBytes(tweak);
return tweak;
}
private int getRadix(String alphabet) {
AlphabetMapper alphabetMapper = new BasicAlphabetMapper(alphabet);
int radix = alphabetMapper.getRadix();
log.info("Radix: {}", radix);
return radix;
}
}
Я не сталкивался с примером того, как его правильно использовать. Похоже, проблема в радиксе. Выполнение вышеуказанных результатов со следующей трассировкой стека:
java.lang.IllegalArgumentException: input data outside of radix
at org.bouncycastle.crypto.fpe.SP80038G.checkData(Unknown Source)
at org.bouncycastle.crypto.fpe.SP80038G.checkArgs(Unknown Source)
at org.bouncycastle.crypto.fpe.SP80038G.encryptFF3_1(Unknown Source)
at org.bouncycastle.crypto.fpe.FPEFF3_1Engine.encryptBlock(Unknown Source)
at org.bouncycastle.crypto.fpe.FPEEngine.processBlock(Unknown Source)
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$BufferedFPEBlockCipher.doFinal(Unknown Source)
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Cipher.java:2164)
С радиусом, установленным, например, 64 или выше, этот код работает, но это больше не шифр FPE — текст содержит символы за пределами области [0-9]. Как это исправить?
Ответ №1:
BouncyCastle может не поддерживать оригинальный FF3, но NIST опубликовал примеры с 8-байтовой настройкой с использованием radix 10 и 256-битного ключа:
Образец № 11
из FF3samples.pdf
FF3-AES256
Ключ EF 43 59 D8 D5 80 AA 4F 7F 03 6D 6F 04 FC 6A 94 2B 7E 15 16 28
AE D2 A6 AB F7 15 88 09 CF 4F
3C Радиус = 10
Настройка D8 E7 92 0A FA 33 0A 73
Обычный текст 890121234567890000
Зашифрованный текст 922011205562777495
FF3-1 все еще является черновиком, и нет опубликованных примеров тестов.
Тестовый класс SP80038GTest.java кажется незавершенным для FF3-1. У них есть все девять тестов NIST для FF1 в образцах FF1[], но только 1 для FF3-1 с 256-битным ключом в образцах FF3_1[]. Вы можете рассмотреть возможность перехода на FF1 BouncyCastles, чтобы обеспечить надлежащее покрытие тестов, или использовать мою реализацию Mysto FF3 и FF3-1.
Вот один тестовый пример в BouncyCastle FF3-1 с 10-байтовым, 7-байтовым и 256-битным ключом:
private FFSample(int radix, byte[] key, byte[] plaintext, byte[] ciphertext, byte[] tweak)
{
...
}
FFSample.from(10, "1A58964B681384806A5A7639915ED0BE837C9C50C150AFD8F73445C0438CACF3", "4752683571", "2234571788", "CE3EBD69454984")