如何在不使用密钥的情况下验证Fernet令牌?

时间:2019-07-11 22:17:24

标签: python python-2.7 cryptography aes

我正在使用cryptography.fernet库对一些小文本进行加密,当我在服务器上收到该文本时,我想先验证该文本实际上是有效的密文,然后再进行RPC解密它,以便我的服务器可以返回该数据无效。是否有一些便宜的方法可以避免垃圾邮件的发生?

例如,如果这是我的密文:

>>> k = Fernet.generate_key()
>>> f = Fernet(k)
>>> c = f.encrypt("misingnoglic@gmail.com")
>>> c
'gAAAAABdCC4Z8fqgRu7fCv2e7cvPm46rMwTVmSJK6guR5vrnvjaCICXKI1cI-_qr3Cs_z602a4tS-sMYm_smSOzgwOJ8biVQDqlyyyt-iLcxQNCOmjBywwM='

有没有便宜的方法可以验证以gAAAA开头的字符串是否有效?还是字符串“ abcde”无效?

谢谢!

1 个答案:

答案 0 :(得分:2)

好吧,看看the Fernet specification。 Fernet令牌(这就是您所说的字符串的调用方式)的结构如下(其中表示串联):

token = urlsafe_b64encode(Version ‖ Timestamp ‖ IV ‖ Ciphertext ‖ HMAC)

HMAC是令牌的最后一部分,它是使用签名密钥(是密钥的前半部分)从初始部分(Version ‖ Timestamp ‖ IV ‖ Ciphertext计算出来的,如规范所记录。

问题是您没有密钥,因此,您唯一可以做的就是在从Base64解码令牌后检查令牌的初始字段:

  • Version应该为0x80,这意味着版本1(同样在规范中有记录)。
  • Timestamp是与令牌关联的时间戳:您可以检查此数字是否在特定范围内以丢弃任何过期或格式错误的令牌。

因此,您可以执行以下操作来“减少垃圾邮件”:

from base64 import urlsafe_b64decode
from struct import unpack
from datetime import datetime, timedelta

bin_token = urlsafe_b64decode(c) # <-- c is the Fernet token you received
version, timestamp = unpack('>BQ', bin_token[:9])

tok_age = datetime.now() - datetime.fromtimestamp(timestamp)
max_age = timedelta(7) # 7 days

if version != 0x80:
    print 'Invalid token version!'

if tok_age > max_age:
    print 'Token expired!'
elif tok_age < timedelta(0):
    print 'Token timestamp in the future! Invalid token!'

奖金:正如我所说,如果您至少没有签名密钥(即密钥的前半部分),则无法验证令牌的有效性。因此,以下内容当然不适用于您的情况,但假设您具有签名密钥。在这种情况下,除了您仍应执行的上述检查之外,您还可以执行以下操作来验证令牌的有效性:

from base64 import urlsafe_b64decode
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.hmac import HMAC
from cryptography.hazmat.backends import default_backend

bin_data = urlsafe_b64decode(c)

# Assuming you have this:
signing_key = "???" # should be urlsafe_b64decode(k)[:16]

client_data = bin_data[:-32]
client_hmac = bin_data[-32:]

print 'Client HMAC:', client_hmac.encode('hex')

real_hmac = HMAC(signing_key, hashes.SHA256(), default_backend())
real_hmac.update(client_data)
real_hmac = real_hmac.finalize()

print 'Real HMAC  :', real_hmac.encode('hex')

if client_hmac == real_hmac:
    print 'Token seems valid!'
else:
    print 'Token does NOT seem valid!'