一、背景
在實際應用中,對數據庫中的信用卡號、身份證號等敏感數據進行加密是非要有必要的,然而使用傳統的分組密碼通常會擴展數據,使數據長度和類型發送變化,需要修改數據庫結構或應用程序來適應這些變化,成本非常高。為了解決這類問題,期望出現加密后的密文和加密前的明文格式一致(長度和字符類型一樣)的加密算法,也就是本文要提到的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