Как мне разобрать данные расширения subjectAltName, используя pyasn1? - PullRequest
16 голосов
/ 02 апреля 2011

У меня есть некоторые данные, которые pyOpenSSL дал мне, '0\r\x82\x0bexample.com'.Это должно быть значение расширения subjectAltName X509.Я попытался закодировать необходимые части спецификации ASN1 для этого расширения, используя pyasn1 (и основываясь на одном из примеров 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())

Очевидно, мне немного скучно в конце, и я не совсемукажите тип 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, будут весьма полезны.

Ответы [ 2 ]

15 голосов
/ 03 апреля 2011

Я разместил этот вопрос в списке пользователей pyasn1, и Илья Этингоф (автор 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 взято из определения ASN.1 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
}

После определения тега для каждого из этих полей componentType, синтаксический анализ завершается успешно:

(GeneralNames().setComponentByPosition(
    0, GeneralName().setComponentByPosition(1, IA5String('example.com'))), '')
0 голосов
/ 07 августа 2018

Опоздав с этим ответом, но вместо написания схемы ASN.1 вручную, вы также можете использовать модуль RF2459, предоставляемый в pyasn1-modules (также автор Илья Этингоф)

Минимально этот код должен работать и, надеюсь, будет достаточно, чтобы вы начали работать с более сложными конструкциями 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']

Если сертификат, с которым вы работаете, имеет несколько расширений, вам нужно будет использовать get_extension_count из объекта X509 и get_short_name из объекта X509Extension, предоставленного в pyopenssl.

...