我有一些pyOpenSSL给我的数据'0\r\x82\x0bexample.com'
。这应该是subjectAltName X509扩展名的值。我尝试使用pyasn1(并基于其中一个pyasn1示例)为此扩展编码ASN1规范的必要部分:
from pyasn1.type import univ, constraint, char, namedtype
from pyasn1.codec.der.decoder import decode
MAX = 64
class DirectoryString(univ.Choice):
componentType = namedtype.NamedTypes(
namedtype.NamedType(
'teletexString', char.TeletexString().subtype(
subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
namedtype.NamedType(
'printableString', char.PrintableString().subtype(
subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
namedtype.NamedType(
'universalString', char.UniversalString().subtype(
subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
namedtype.NamedType(
'utf8String', char.UTF8String().subtype(
subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
namedtype.NamedType(
'bmpString', char.BMPString().subtype(
subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
namedtype.NamedType(
'ia5String', char.IA5String().subtype(
subtypeSpec=constraint.ValueSizeConstraint(1, MAX))),
)
class AttributeValue(DirectoryString):
pass
class AttributeType(univ.ObjectIdentifier):
pass
class AttributeTypeAndValue(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('type', AttributeType()),
namedtype.NamedType('value', AttributeValue()),
)
class RelativeDistinguishedName(univ.SetOf):
componentType = AttributeTypeAndValue()
class RDNSequence(univ.SequenceOf):
componentType = RelativeDistinguishedName()
class Name(univ.Choice):
componentType = namedtype.NamedTypes(
namedtype.NamedType('', RDNSequence()),
)
class Extension(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('extnID', univ.ObjectIdentifier()),
namedtype.DefaultedNamedType('critical', univ.Boolean('False')),
namedtype.NamedType('extnValue', univ.OctetString()),
)
class Extensions(univ.SequenceOf):
componentType = Extension()
sizeSpec = univ.SequenceOf.sizeSpec + constraint.ValueSizeConstraint(1, MAX)
class GeneralName(univ.Choice):
componentType = namedtype.NamedTypes(
# namedtype.NamedType('otherName', AnotherName()),
namedtype.NamedType('rfc822Name', char.IA5String()),
namedtype.NamedType('dNSName', char.IA5String()),
# namedtype.NamedType('x400Address', ORAddress()),
namedtype.NamedType('directoryName', Name()),
# namedtype.NamedType('ediPartyName', EDIPartyName()),
namedtype.NamedType('uniformResourceIdentifier', char.IA5String()),
namedtype.NamedType('iPAddress', univ.OctetString()),
namedtype.NamedType('registeredID', univ.ObjectIdentifier()),
)
class GeneralNames(univ.SequenceOf):
componentType = GeneralName()
sizeSpec = univ.SequenceOf.sizeSpec + constraint.ValueSizeConstraint(1, MAX)
class SubjectAltName(GeneralNames):
pass
print decode('0\r\x82\x0bexample.com', asn1Spec=GeneralNames())
显然,我在结尾附近有点无聊,并没有完全指定GeneralName
类型。但是,测试字符串应该包含dNSName
,而不是其中一个跳过的值,所以我希望它没关系。
当程序运行时,它失败并出现我无法解释的错误:
Traceback (most recent call last):
File "x509.py", line 94, in <module>
print decode('0\r\x82\x0bexample.com', asn1Spec=GeneralNames())
File "/usr/lib/pymodules/python2.6/pyasn1/v1/codec/ber/decoder.py", line 493, in __call__
length, stGetValueDecoder, decodeFun
File "/usr/lib/pymodules/python2.6/pyasn1/v1/codec/ber/decoder.py", line 202, in valueDecoder
substrate, asn1Spec
File "/usr/lib/pymodules/python2.6/pyasn1/v1/codec/ber/decoder.py", line 453, in __call__
__chosenSpec.getTypeMap().has_key(tagSet):
File "/usr/lib/pymodules/python2.6/pyasn1/v1/type/univ.py", line 608, in getTypeMap
return Set.getComponentTypeMap(self)
File "/usr/lib/pymodules/python2.6/pyasn1/v1/type/univ.py", line 535, in getComponentTypeMap
def getComponentTypeMap(self): return self._componentType.getTypeMap(1)
File "/usr/lib/pymodules/python2.6/pyasn1/v1/type/namedtype.py", line 126, in getTypeMap
'Duplicate type %s in map %s'%(k,self.__typeMap)
pyasn1.error.PyAsn1Error: Duplicate type TagSet(Tag(tagClass=0, tagFormat=0, tagId=22)) in map {TagSet(Tag(tagClass=0, tagFormat=0, tagId=22)): IA5String()}
关于我出错的地方以及如何使用pyasn1成功解析此扩展类型的任何提示都将非常感激。
答案 0 :(得分:15)
我在pyasn1-users列表上发布了这个问题,而Ilya Etingof(pyasn1的作者)指出了我的错误。简而言之,NamedType
中的每个GeneralName.componentType
都需要获得标记信息。这是使用subtype
方法完成的。例如,而不是:
namedtype.NamedType('rfc822Name', char.IA5String()),
定义应该是:
namedtype.NamedType('rfc822Name', char.IA5String().subtype(
implicitTag=tag.Tag(tag.tagClassContext,
tag.tagFormatSimple, 1))),
其中1
来自 GeneralName 的ASN.1定义:
GeneralName ::= CHOICE {
otherName [0] OtherName,
rfc822Name [1] IA5String,
dNSName [2] IA5String,
x400Address [3] ORAddress,
directoryName [4] Name,
ediPartyName [5] EDIPartyName,
uniformResourceIdentifier [6] IA5String,
iPAddress [7] OCTET STRING,
registeredID [8] OBJECT IDENTIFIER
}
为componentType
的每个字段定义标记后,解析成功:
(GeneralNames().setComponentByPosition(
0, GeneralName().setComponentByPosition(1, IA5String('example.com'))), '')
答案 1 :(得分:0)
此答案来得很晚,但是您也可以使用pyasn1-modules(同样由Ilya Etingof创作)中提供的RF2459模块来代替手工编写ASN.1模式。
至少此代码应该起作用,并希望足以使您开始使用更复杂的ANS.1构造。确保已运行pip install pyasn1
,pip install pyasn1-modules
和pip install pyopenssl
,否则会出现导入错误。
# Import pyasn and the proper decode function
import pyasn1
from pyasn1.codec.der.decoder import decode as asn1_decoder
# Import SubjectAltName from rfc2459 module
from pyasn1_modules.rfc2459 import SubjectAltName
# Import native Python type encoder
from pyasn1.codec.native.encoder import encode as nat_encoder
# Import OpenSSL tools for working with certs.
from OpenSSL import crypto
# Read raw certificate file
with open('PATH/TO/CERTIFICATE.crt', 'r') as cert_f:
raw_cert = cert_f.read()
cert = crypto.load_certificate(crypto.FILETYPE_PEM, raw_cert)
# Note this example assumes SubjectAltName is the only Extension for this cert.
raw_alt_names = cert.get_extension(0).get_data()
decoded_alt_names, _ = asn1_decoder(raw_alt_names, asn1Spec=SubjectAltName())
# Unless a raw string of ASN.1 is what you need encode back to native Python types
py_alt_names = nat_encoder(decoded_alt_names)
# And Finally a plain Python list of UTF-8 encoded strings representing the SubjectAltNames
subject_alt_names = [ x['dNSName'].decode('utf-8') for x in py_alt_names]
其输出将类似于
['cdn1.example.com', 'cdn2.example.com']
如果您正在使用的证书具有多个扩展名,则需要使用pyopenssl提供的X509对象中的get_extension_count和X509Extension对象中的get_short_name。