我对我需要在这里为 Python 做的事情有点困惑,但是从用于验证具有 YubiOTP 的 Yubikey 的 Yubikey API 文档中,需要以特定的方式生成 HMAC 签名 - 从他们的文档中:
生成签名
该协议使用 HMAC-SHA-1 签名。要使用的 HMAC 密钥是客户端 API 密钥。
在消息中的参数上生成签名。每条消息都包含一组键/值对,并且签名始终覆盖整个集合(不包括签名本身),并按键的字母顺序排序。更准确地说,要生成消息签名,请执行以下操作:
按键顺序按字母顺序对键/值对集进行排序。
构造一行,每个有序键/值对使用 & 连接,每个键和值使用 连接
=
。不要添加任何换行符。不要添加空格。例如:a=2&b=1&c=3
。使用 API 密钥作为密钥,将 HMAC-SHA-1 算法作为八位字节字符串应用在线路上(记得对从 Yubico 获得的 API 密钥进行 base64decode)。
Base 64 根据 RFC 4648 对结果值进行编码,例如
t2ZMtKeValdA+H0jVpj3LIichn4=
.将键 h 下的值附加到消息中。
现在我从他们的文档中对他们的 API 的理解说明了以下有效的请求参数:
id
- Yubico API 的客户端 IDotp
- 来自 yubikey 的 YubiOTP 组件的 YubiOTP 值。h
- 请求的 HMAC-SHA1 签名timestamp
- empty 什么都不做,1
在服务器回复中包含时间戳nonce
- 包含随机唯一数据的 16 到 40 个字符长的字符串。sl
- 一个 0 到 100 的值,表示客户端所需的同步百分比,或字符串“快速”或“安全”以使用服务器值;如果不存在的服务器决定timeout
- 等待同步响应的秒数;让服务器决定是否缺席。
我总共尝试使用两个函数来尝试处理所有这些事情并生成 URL。即,我们支持 HMAC 功能并verify_url_generate
生成 URL(并且API_KEY
是静态编码的 - 我来自 Yubico 的 API 密钥):
def generate_signature(message, key=base64.b64decode(API_KEY)):
message = bytes(message, 'UTF-8')
digester = hmac.new(key, message, hashlib.sha1)
digest = digester.digest()
signature = base64.urlsafe_b64encode(digest)
return str(signature, 'UTF-8')
def verify_url_generate(otp):
nonce = "".join(secrets.choice(ascii_lowercase) for _ in range(40))
data = OrderedDict(
{
"id": None,
"nonce": None,
"otp": None,
"sl": 50,
"timeout": 10,
"timestamp": 1
}
)
data['otp'] = otp
data['id'] = CLIENT_ID
data['nonce'] = nonce
args = ""
for key, value in data.items():
args += f"{key}={value}&"
sig = generate_signature(args[:-1])
url = YUBICO_API_URL + args + "&h=" + sig
print(url)
return
由此生成的任何 URL 都会触发来自远程站点的关于“BAD_SIGNATURE”的通知 - 任何生成的 URL 减去 HMAC sig ( h=
) 参数都有效。所以我们知道问题不在于 URL,而在于 HMAC 签名。
有谁知道我的 HMAC 生成方法做错了什么,通过将 HMAC sig 生成器从有序 dict 中的串联参数传递key=value
给&
每个参数格式?