【问题标题】:Format Preserving Encryption (FPE) in Java with Bouncycastle使用 Bouncycastle 在 Java 中进行格式保留加密 (FPE)
【发布时间】:2021-11-12 11:56:36
【问题描述】:

Release notes 用于 Bouncycastle 版本:1.69(2021 年 6 月 7 日)状态:

SP 800-38G 中两种 FPE 算法 FF1 和 FF3-1 的实现已添加到轻量级 API 和 JCE 提供程序中。

这些可以在bcprov-jdk15onJAR 中找到。

这是尝试使用它的代码:

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] 范围之外的字符。如何解决?

【问题讨论】:

    标签: java encryption cryptography bouncycastle encryption-symmetric


    【解决方案1】:

    BouncyCastle 可能不支持原始 FF3,但 NIST 已发布使用基数 10 和 256 位密钥进行 8 字节调整的示例:

    样本 #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 仍是草稿,没有发布测试examples

    FF3-1 的测试类SP80038GTest.java 似乎还没有完成。他们在 ff1Samples[] 中有 FF1 的所有 9 个 NIST 测试用例,但在 ff3_1Samples[] 中只有 1 个用于 FF3-1 的 256 位密钥。您可以考虑切换到 BouncyCastles 的 FF1 以获得适当的测试覆盖率或使用我的 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")
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-22
      • 1970-01-01
      • 2021-03-22
      • 2012-05-22
      • 2013-09-22
      • 1970-01-01
      • 1970-01-01
      • 2013-07-03
      相关资源
      最近更新 更多