Python新ASN.1 API预览
如果您曾使用Python处理密码学、PKI方案或低级网络,很可能遇到过ASN.1。ASN.1支撑着每个TLS握手(通过X.509路径验证),为LDAP、SNMP和3GPP等核心互联网协议提供序列化层,并作为密码原语和协议表示的通用语言。
ASN.1的关键作用与其丰富的安全历史相辅相成:ASN.1编码规则的实现历来是内存损坏和拒绝服务漏洞的丰富来源。同样,ASN.1在互联网协议最底层的存在使得性能和无解析差异成为关键要求。
Python有多个优秀的ASN.1实现(如pyasn1、asn1和asn1tools),但这些通常属于后一类:纯Python编写使得性能成为问题,集成到使用其他ASN.1解析器的堆栈(例如在X.509层)会引入差异风险。
我们正在改变这一点:在Alpha-Omega的资助下,我们正在为PyCA Cryptography构建一个ASN.1 API,解决Python生态系统当前的三个关键缺陷:
- 性能:新API将使用纯Rust ASN.1解析器,提供接近原生的解析性能。
- 差异减少:上述解析器已用于PyCA Cryptography的X.509 API。这将减少“混合匹配”ASN.1解析方法的需求,从而减少差异漏洞。
- 现代化:新API将暴露声明式数据类样式接口,充满类型提示,使其熟悉、符合习惯并与类型检查器兼容。
例如,像这样的ASN.1定义:
1
2
3
4
5
6
7
|
Doohickies ::= SEQUENCE {
tschotchkes OCTET STRING,
baubles INTEGER,
knickknacks UTF8String,
whatchamacallits SEQUENCE OF OBJECT IDENTIFIER,
gizmos SET OF GeneralizedTime OPTIONAL
}
|
…将对应以下Python代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from datetime import datetime
from cryptography.hazmat import asn1
@asn1.sequence
class Doohickies:
tschotchkes: bytes
baubles: int
knickknacks: str
whatchamacallits: list[asn1.ObjectIdentifier]
gizmos: set[datetime] | None
doohickies = Doohickies.from_der(b"...")
print(doohickies.tschotchkes)
doohickies.to_der() # b"..."
|
这项工作是我们之前由Sovereign Tech Fund资助的X.509路径验证工作的逻辑延续。它反映了我们持续改进Python生态系统的承诺,特别是在密码学和供应链安全领域。
如果您有兴趣了解更多或资助类似工作,请联系我们!
ASN.1快速背景
ASN.1,或抽象语法记法一,是一种接口描述语言(IDL)。这是一种花哨的说法,表示它是一种以语言和平台无关的方式描述数据结构的语法。
令人困惑的是,ASN.1本身不是序列化格式。相反,它定义编码规则,这些规则进而定义在不同设置中ASN.1结构的序列化和反序列化。在实践中,ASN.1与可分辨编码规则(DER)同义。
我们将在这篇文章中互换使用“ASN.1”和“DER”。与其深入探讨两者的复杂性(Let’s Encrypt有很好的覆盖),我们将专注于使DER几十年来保持相关性的属性:
- DER是规范编码:在DER中编码给定ASN.1结构只有一种方式。换句话说,ASN.1结构在DER中的编码是确定性的,并且可以在保留逐位相等性的情况下往返。
- DER相对紧凑:DER定义二进制格式,并且由于是规范的,禁止整数、布尔值和时间的非最小编码。
- DER是自描述和自定界编码:给定的DER消息可以完全且健全地解析,无需事先参考模式或格式描述,除了DER本身的编码规则。
这些属性自然适用于Web开发人员所称的“渐进增强”:使用DER的应用程序可以解码它关心的特定结构,同时跳过它不关心的结构,仅解码它们的长度以跳转到下一个。
DER支持任意精度整数:DER中的INTEGER类型在功能上大小不受限制,这使得它适合表示密码学设置中经常出现的大数字(例如素数)。
总之,这些属性使DER在密码学、网络和电信设置中非常流行。
更准确地说,它在这些设置的内部非常流行:ASN.1用于保护世界TLS流量的X.509证书,广泛用于PEM编码格式,并为互联网的许多低级协议层提供描述和序列化。
为Python开发ASN.1库的动机
您可能会合理地问:为什么Python需要这个?
毕竟,大多数Python开发人员不会每天接触ASN.1,而那些接触的人大多以预定义的方式(如X.509证书)进行。为什么生态系统需要对ASN.1的通用支持?
答案在于,无论好坏,有许多情况下Python开发人员需要在X.509和其他知名格式和协议的“标准”形状之外进行ASN.1编码和解码。
这可以在Sigstore生态系统中看到:Sigstore主要是一个普通的RFC 5280风格PKI,但它也包括一些自定义X.509扩展用于其自身目的。例如,Sigstore日志条目的摘录显示以下扩展:
1
2
3
4
5
|
Runner Environment: github-hosted
Source Repository URI: https://github.com/pypa/sampleproject
Source Repository Ref: refs/heads/main
Source Repository Owner URI: https://github.com/pypa
|
如果我们想从Python使用这些(例如,为了根据策略验证Sigstore证书),我们需要提取它们:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
from cryptography import x509
raw_cert = b"""
-----BEGIN CERTIFICATE-----
MIIGoTCCBiigAwIBAgITFai+PDKak1xA1HLq0mskqhDV5zAKBggqhkjOPQQDAzA3
MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxHjAcBgNVBAMTFXNpZ3N0b3JlLWludGVy
bWVkaWF0ZTAeFw0yNDExMDYyMjM3MDdaFw0yNDExMDYyMjQ3MDdaMAAwWTATBgcq
hkjOPQIBBggqhkjOPQMBBwNCAARbx1Fse2Ln00On5aFaL+lHNGFYLaqeKDduplZD
PJS+w2PjYfNPL0g/n4sDWEQFZfyIExEWKulZ2GKNzAc0+SmUo4IFSDCCBUQwDgYD
VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBT/uSEI
XmQzuRkppWXrTKVkfZFJbzAfBgNVHSMEGDAWgBTf0+nPViQRlvmo2OkoVaLGLhhk
PzBhBgNVHREBAf8EVzBVhlNodHRwczovL2dpdGh1Yi5jb20vcHlwYS9zYW1wbGVw
cm9qZWN0Ly5naXRodWIvd29ya2Zsb3dzL3JlbGVhc2UueW1sQHJlZnMvaGVhZHMv
bWFpbjA5BgorBgEEAYO/MAEBBCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVi
dXNlcmNvbnRlbnQuY29tMBIGCisGAQQBg78wAQIEBHB1c2gwNgYKKwYBBAGDvzAB
AwQoNjIxZTQ5NzRjYTI1Y2U1MzE3NzNkZWY1ODZiYTNlZDhlNzM2YjNmYzAVBgor
BgEEAYO/MAEEBAdSZWxlYXNlMCAGCisGAQQBg78wAQUEEnB5cGEvc2FtcGxlcHJv
amVjdDAdBgorBgEEAYO/MAEGBA9yZWZzL2hlYWRzL21haW4wOwYKKwYBBAGDvzAB
CAQtDCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVi
|