一种保留格式的加密算法FPE

一、背景 

      在实际应用中,对数据库中的信用卡号、身份证号等敏感数据进行加密是非要有必要的,然而使用传统的分组密码通常会扩展数据,使数据长度和类型发送变化,需要修改数据库结构或应用程序来适应这些变化,成本非常高。为了解决这类问题,期望出现加密后的密文和加密前的明文格式一致(长度和字符类型一样)的加密算法,也就是本文要提到的FPE(format-preserving encryption)算法。

      FPE算法的初衷是为了解决数据库或者应用系统中敏感数据的加密问题,随着研究的进展,其应用并不仅限于此,比如FPE可以应用于数据遮蔽(data masking)领域,通过克隆原始数据进行掩码转换,输出一个与元数据格式、关联一模一样的数据,用于解决从生产环境的数据向测试环境(或者开发环境)导入时可能产生的数据内容、数据安全问题,此外,FPE对于网络数据安全一样有用,可以使数据报在不改变格式的情况下在传输过程中受到保护。

二、特征

1、数据不能被扩充. 例如当加密N位的数字时,必须输出另外一个N位的数字

2、数据类型不能被改变. 例如一个只包含数字的串加密后输出的串也只能是数字

3、数据必须能被确定性加密.例如对于数据库中作为主键或者索引字段的数据,被加密后将保留其所在的列作为主键或者索引的特性

三、构建方法

学术界关于格式保留加密的研究已持续多年,2002年,Black和Rogaway提出了3种FPE构建方法:

1、 Prefix

2、Cycle-Walking

3、Generalized-Feistel

这三种方法成为构造FPE模型的基本方法,其中Generalized-Feistel方法的适用性更为广泛,其核心思路是基于Feistel网络来构建符合整数集大小的分组密码,并结合Cycle-Walking方法使最终密文输出在合理范围内,Feistel网络可以通过定义分组大小、密钥长度、轮次数、子密钥生成、轮函数等来构造一个分组密码。

具体文献:https://www.docin.com/p-132469640.html

四、实战代码

@UtilityClass
public class FPEncryptionUtils {

    private static final String SECRET_KEY="yoursecuritypriv";

    private static final byte[] A_TWEAK_SUFFIX ="yoursecuritypriv".getBytes();

    private static final Alphabet EXTEND_ALPHABET=new ExtendAlphabet();

    private static final TextToIntTransformer TEXT_TO_INT_TRANSFORMER = new GenericTransformations(EXTEND_ALPHABET.availableCharacters());

    private static final IntToTextTransformer INT_TO_TEXT_TRANSFORMER = new GenericTransformations(EXTEND_ALPHABET.availableCharacters());

    private static final ThreadLocal<FormatPreservingEncryption> SEED_POOL= ThreadLocal.withInitial(
            () -> FormatPreservingEncryptionBuilder
                    .ff1Implementation()
                    .withDomain(new GenericDomain(EXTEND_ALPHABET,TEXT_TO_INT_TRANSFORMER,INT_TO_TEXT_TRANSFORMER))
                    .withDefaultPseudoRandomFunction(SECRET_KEY.getBytes())
                    .withDefaultLengthRange()
                    .build());

    public static String encrypt(String plainText){
        if(StringUtils.isEmpty(plainText)){
            return null;
        }
        return SEED_POOL.get().encrypt(plainText,A_TWEAK_SUFFIX);
    }

    public static String decrypt(String cipherText){
        if(StringUtils.isEmpty(cipherText)){
            return null;
        }
        return SEED_POOL.get().decrypt(cipherText,A_TWEAK_SUFFIX);
    }

    private static class ExtendAlphabet implements Alphabet{

        private static final char[] NUM_AND_CHARACTER_CHARS = new char[] {
                '1', '2', '3', '4', '5', '6', '7', '8','9','0',
                'A','B','C','D','E','F','G','H','I','J','K','L',
                'M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};

        private static final char[] ONLY_NUM=new char[]{
            '1', '2', '3', '4', '5', '6', '7', '8','9','0'
        };        

        @Override
        public char[] availableCharacters() {
            return NUM_AND_CHARACTER_CHARS;
        }

        @Override
        public Integer radix() {
            return NUM_AND_CHARACTER_CHARS.length;
        }
    }
}

五、效果展示

输入:1008611           输出:8415027
输入:18815658640       输出:06207345474
输入:10086CHF11LH      输出:ZJ8VAT6W7WRL
posted @ 2020-06-03 16:30  龙须子  阅读(4569)  评论(0编辑  收藏  举报