如何对像“A123456”这样的文本进行FPE(格式保留加密),如果加密保留了格式,我应该得到一个字符长度超过6位的字符串,例如“Z655432”,我使用了python库FF3,所以我将字符串“A123456”拆分为“A”和“123456”,并将 FF3cipher 应用于每个字符串,在这里我发现最小字符数问题,所以我必须对“A”进行一些填充,问题是当你这样做时填充你丢失了格式,你不会得到你想要的格式。
我尝试了填充,但格式没有保留,我尝试了没有最低字符要求的 pyffx 库,但显然它不安全,它没有 NIST 要求
from ff3 import FF3Cipher
key = "2DE79D232DF5585D68CE47882AE256D8"
tweak = "CB92D080979564"
alphabet = "AZERTYUIOPQSDFGHJKLMWXCVBN"
cipher = FF3Cipher.withCustomAlphabet(key, tweak, alphabet)
def pad_to_min_length_alp(text, min_len):
pad_char = 'A'
if len(text) < min_len:
return text + pad_char * (min_len - len(text))
return text
plaintext = 'A'
padded_plaintext = pad_to_min_length_alp(plaintext, cipher.minLen)
ciphertext = cipher.encrypt(padded_plaintext)
print(f"Encrypted value: {ciphertext}")
结果:加密值:SPVVY
所以A已被加密为5个字符的字符串
格式未保留
我注意到对于某些用户来说,在原始消息(1 个字母 + 6 个十进制数字)、[0, 26000000) 中的数字和基数为 72 的 4 位数字的字符串之间进行转换并不简单。这激励我回答这个问题。
消息空间的大小为26000000,所以需要加密[0, 26000000)中的一个整数。但这不是整数的幂,您需要循环行走,而不是分裂。我建议使用基数 72,因为 724 接近且大于 26000000。
因为
ff3
库不支持数字作为输入,所以你应该将原始消息转换为数字,然后将其转换为基数为72的4位数字的字符串。设基数72的数字为''.join(chr(e) for e in range(72))
。例如,在加密 'Z123456'
时,它会先转换为 25123456 = 67*72**3 + 22*72**2 + 24*72 + 64
,然后再转换为 '\x43\x16\x18\x40'
。您需要使用 ff3
加密该字符串,并将输出密文转换为数字。如果解码后的数字大于26000000 - 1
,则需要重新加密。这就是循环行走。
以下是一个实现示例。
import math
import ff3
RADIX = 72
MAX_NUM = 26*1000000
N_DIGITS = math.ceil(math.log(MAX_NUM) / math.log(RADIX))
DIGITS = ''.join(chr(e) for e in range(RADIX))
def msg_to_num(msg):
return (ord(msg[0]) - ord('A')) * 1000000 + int(msg[1:])
def num_to_msg(num):
q, r = divmod(num, 1000000)
return '%c%06d' % (q + ord('A'), r)
def num_to_digits(num):
digits = [None] * N_DIGITS
for i in range(N_DIGITS):
num, r = divmod(num, RADIX)
digits[N_DIGITS-1 - i] = DIGITS[r]
return ''.join(digits)
def digits_to_num(digits):
num = 0
for i in range(N_DIGITS):
num = num * RADIX + DIGITS.index(digits[i])
return num
cipher = ff3.FF3Cipher.withCustomAlphabet(
b'key0123456789012'.hex(), b'tweak012'.hex(), DIGITS)
def encrypt(pt):
num = msg_to_num(pt)
i = 0
while True:
num = digits_to_num(cipher.encrypt(num_to_digits(num)))
i += 1
if num < MAX_NUM:
break
return num_to_msg(num)
def decrypt(ct):
num = msg_to_num(ct)
i = 0
while True:
num = digits_to_num(cipher.decrypt(num_to_digits(num)))
i += 1
if num < MAX_NUM:
break
return num_to_msg(num)
pt = 'Z123456'
ct = encrypt(pt)
print('ct:', ct)
pt2 = decrypt(ct)
print('pt2:', pt2)