一种保留格式的加密算法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
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是一个使用 FPE 算法进行手机号脱敏的 Python 示例代码,可以保持加密后的手机号与原手机号的格式相同: ```python import hashlib import hmac import base64 def fpe_encrypt(plaintext, key, radix=10, rounds=4): # 对明文进行哈希 hash_val = hmac.new(key.encode(), plaintext.encode(), hashlib.sha256).digest() # 将哈希值转换为整数 num_val = int.from_bytes(hash_val, byteorder='big') # 将整数转换为指定进制的字符串 str_val = '' while num_val > 0: num_val, rem = divmod(num_val, radix) str_val = str(rem) + str_val # 对字符串进行 FPE 加密 for i in range(rounds): str_val = ''.join([chr((ord(c) - ord('0') + i) % (radix - 1) + ord('0')) for c in str_val]) # 将加密后的字符串填充到与原始字符串相同的长度 encrypted = str_val.zfill(len(plaintext)) # 返回加密后的字符串 return encrypted # 测试代码 plaintext = '13800138000' key = 'my-secret-key' encrypted = fpe_encrypt(plaintext, key) print('原始手机号:', plaintext) print('加密后手机号:', encrypted) ``` 该代码实现了与前面代码类似的 FPE 加密算法,不同之处在于加密后的字符串需要填充到与原始字符串相同的长度。这里使用了字符串的 `zfill()` 方法来实现填充,该方法可以在字符串左侧填充指定数量的零,使得字符串的总长度等于指定长度。 需要注意的是,这里的 FPE 加密算法同样采用了一个简单的转换方式,安全性可能不够高。在实际应用中,可以考虑使用更加复杂的 FPE 算法,或者结合其他加密算法来提高安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值