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