python - ¿Cómo analizo los datos de la extensión subjectAltName usando pyasn1?
ssl x509 (1)
Tengo algunos datos que pyOpenSSL me dio, ''0/r/x82/x0bexample.com''
. Este debería ser el valor de una extensión subjectAltName X509. Traté de codificar las partes necesarias de la especificación ASN1 para esta extensión usando pyasn1 (y en base a uno de los ejemplos de pyasn1):
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())
Claramente, me aburrí un poco cerca del final y no especifiqué por completo el tipo de nombre general. Sin embargo, la cadena de prueba debe contener un dNSName
, no uno de los valores omitidos, así que espero que no importe.
Cuando se ejecuta el programa, falla con un error que no puedo interpretar:
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()}
Me gustaría mucho cualquier consejo sobre dónde me equivoqué y cómo analizar correctamente este tipo de extensión con pyasn1.
Publiqué esta pregunta en la lista de usuarios de pyasn1 e Ilya Etingof (el autor de pyasn1) señaló mi error. En resumen, cada NamedType
en GeneralName.componentType
necesita recibir información de etiqueta. Esto se hace con el método de subtype
. Por ejemplo, en lugar de:
namedtype.NamedType(''rfc822Name'', char.IA5String()),
la definición debería ser:
namedtype.NamedType(''rfc822Name'', char.IA5String().subtype(
implicitTag=tag.Tag(tag.tagClassContext,
tag.tagFormatSimple, 1))),
donde 1
proviene de la definición ASN.1 de GeneralName :
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
}
Después de definir una etiqueta para cada uno de estos campos del componentType
, el análisis tiene éxito:
(GeneralNames().setComponentByPosition(
0, GeneralName().setComponentByPosition(1, IA5String(''example.com''))), '''')