You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask应用中Yubikey凭据JSON存储及反序列化认证报错解决

解决fido2 AttestedCredentialData序列化/反序列化JSON时的"Not Enough Data to Unpack"错误

问题原因

JSON仅支持序列化基本数据类型(字符串、数字、列表、字典),而AttestedCredentialData对象包含自定义类型(AAGUID)和字节类型(credential_id、公钥中的-2/-3字段)。直接将该对象序列化到JSON会导致数据格式损坏或丢失,反序列化时无法还原出完整的字节流供fido2的unpack()方法解析,从而抛出"Not Enough Data to Unpack"错误。

解决方案:正确序列化与反序列化

利用fido2库内置的pack()/unpack()方法处理对象的字节流转换,再通过Base64编码将字节流转为JSON可存储的字符串。

1. 注册时:序列化并存储到JSON

import json
import base64
from fido2.ctap2 import AttestedCredentialData

# 假设已获取注册流程返回的attested_credential_data对象
attested_credential_data = ...

# 将AttestedCredentialData转为标准字节流
acd_bytes = attested_credential_data.pack()

# 字节流转Base64字符串(确保JSON可存储)
acd_b64 = base64.b64encode(acd_bytes).decode('utf-8')

# 存储到JSON文件(关联用户名)
user_data = {
    "username": "example_user",
    "registered_credential": acd_b64
}

with open("users.json", "w") as f:
    json.dump(user_data, f)

2. 登录时:从JSON取回并反序列化

import json
import base64
from fido2.ctap2 import AttestedCredentialData

# 读取JSON中的用户数据
with open("users.json", "r") as f:
    user_data = json.load(f)

# 解码Base64字符串为字节流
acd_b64 = user_data["registered_credential"]
acd_bytes = base64.b64decode(acd_b64)

# 反序列化为AttestedCredentialData对象
attested_credential_data = AttestedCredentialData.unpack(acd_bytes)

# 后续可使用该对象完成认证流程

备选方案:拆解字段存储(调试场景)

如果需要单独存储各个字段,可拆解对象并将所有字节类型转Base64:

# 序列化
user_data = {
    "username": "example_user",
    "aaguid": str(attested_credential_data.aaguid),
    "credential_id": base64.b64encode(attested_credential_data.credential_id).decode('utf-8'),
    "public_key": {
        k: base64.b64encode(v).decode('utf-8') if isinstance(v, bytes) else v
        for k, v in attested_credential_data.public_key.items()
    }
}

# 反序列化
from fido2.utils import AAGUID
from fido2.cose import CoseKey

aaguid = AAGUID.from_string(user_data["aaguid"])
credential_id = base64.b64decode(user_data["credential_id"])
public_key_dict = {
    int(k): base64.b64decode(v) if isinstance(v, str) else int(v)
    for k, v in user_data["public_key"].items()
}
public_key = CoseKey.from_dict(public_key_dict)

attested_credential_data = AttestedCredentialData(aaguid, credential_id, public_key)

关键注意事项

  • 必须使用fido2官方提供的pack()/unpack()方法处理AttestedCredentialData对象,确保字节流符合CTAP2协议规范。
  • 所有字节类型数据必须通过Base64编码转为字符串后再存入JSON,避免直接序列化bytes导致的格式错乱。
  • 不要手动修改或拼接AttestedCredentialData的字节流,否则会破坏协议结构,导致解析失败。

内容的提问来源于stack exchange,提问作者Krithick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 15:27:45