All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dromara.hutool.crypto.symmetric.FPE Maven / Gradle / Ivy

There is a newer version: 6.0.0.M3
Show newest version
/*
 * Copyright (c) 2013-2024 Hutool Team and hutool.cn
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.dromara.hutool.crypto.symmetric;

import org.dromara.hutool.crypto.KeyUtil;
import org.dromara.hutool.crypto.Padding;
import org.bouncycastle.crypto.AlphabetMapper;
import org.bouncycastle.jcajce.spec.FPEParameterSpec;

import java.io.Serializable;

/**
 * FPE(Format Preserving Encryption)实现,支持FF1和FF3-1模式。
* 相关介绍见:https://anquan.baidu.com/article/193 * *

* FPE是一种格式保持与明文相同的加密方式,通常用于数据脱敏中,因为它需要保持明密文的格式相同, * 例如社保号经过加密之后并不是固定长度的杂文,而是相同格式、打乱的号码,依然是社保号的格式。 *

*

* FPE算法可以保证: * *

    *
  • 数据长度不变。加密前长度是N,加密后长度仍然是N
  • *
  • 数据类型不变,加密前是数字类型,加密后仍然是数字类型
  • *
  • 加密过程可逆,加密后的数据可以通过密钥解密还原原始数据
  • *
*

* 此类基于BouncyCastle实现。 * * @author looly * @since 5.7.12 */ public class FPE implements Serializable { private static final long serialVersionUID = 1L; // 映射字符表,规定了明文和密文的字符范围 private final AES aes; private final AlphabetMapper mapper; /** * 构造,使用空的Tweak * * @param mode FPE模式枚举,可选FF1或FF3-1 * @param key 密钥,{@code null}表示随机密钥,长度必须是16bit、24bit或32bit * @param mapper Alphabet字典映射,被加密的字符范围和这个映射必须一致,例如手机号、银行卡号等字段可以采用数字字母字典表 */ public FPE(final FPEMode mode, final byte[] key, final AlphabetMapper mapper) { this(mode, key, mapper, null); } /** * 构造 * * @param mode FPE模式枚举,可选FF1或FF3-1 * @param key 密钥,{@code null}表示随机密钥,长度必须是16bit、24bit或32bit * @param mapper Alphabet字典映射,被加密的字符范围和这个映射必须一致,例如手机号、银行卡号等字段可以采用数字字母字典表 * @param tweak Tweak是为了解决因局部加密而导致结果冲突问题,通常情况下将数据的不可变部分作为Tweak,{@code null}使用默认长度全是0的bytes */ public FPE(FPEMode mode, final byte[] key, final AlphabetMapper mapper, byte[] tweak) { if (null == mode) { mode = FPEMode.FF1; } if (null == tweak) { switch (mode) { case FF1: tweak = new byte[0]; break; case FF3_1: // FF3-1要求必须为56 bits tweak = new byte[7]; } } this.aes = new AES(mode.value, Padding.NoPadding.name(), KeyUtil.generateKey(mode.value, key), new FPEParameterSpec(mapper.getRadix(), tweak)); this.mapper = mapper; } /** * 加密 * * @param data 数据,数据必须在构造传入的{@link AlphabetMapper}中定义的范围 * @return 密文结果 */ public String encrypt(final String data) { if (null == data) { return null; } return new String(encrypt(data.toCharArray())); } /** * 加密 * * @param data 数据,数据必须在构造传入的{@link AlphabetMapper}中定义的范围 * @return 密文结果 */ public char[] encrypt(final char[] data) { if (null == data) { return null; } // 通过 mapper 将密文输出处理为原始格式 return mapper.convertToChars(aes.encrypt(mapper.convertToIndexes(data))); } /** * 解密 * * @param data 密文数据,数据必须在构造传入的{@link AlphabetMapper}中定义的范围 * @return 明文结果 */ public String decrypt(final String data) { if (null == data) { return null; } return new String(decrypt(data.toCharArray())); } /** * 加密 * * @param data 密文数据,数据必须在构造传入的{@link AlphabetMapper}中定义的范围 * @return 明文结果 */ public char[] decrypt(final char[] data) { if (null == data) { return null; } // 通过 mapper 将密文输出处理为原始格式 return mapper.convertToChars(aes.decrypt(mapper.convertToIndexes(data))); } /** * FPE模式
* FPE包括两种模式:FF1和FF3(FF2弃用),核心均为Feistel网络结构。 * * @author looly */ public enum FPEMode { /** * FF1模式 */ FF1("FF1"), /** * FF3-1 模式 */ FF3_1("FF3-1"); private final String value; FPEMode(final String name) { this.value = name; } /** * 获取模式名 * * @return 模式名 */ public String getValue() { return value; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy