security encoding binary asn.1

security - ¿Cómo codifica ASN.1 un identificador de objeto?



encoding binary (4)

Codificación OID para dummies :):

  • cada componente OID está codificado en uno o más bytes (octetos)
  • La codificación OID es solo una concatenación de estas codificaciones de componentes OID
  • Los dos primeros componentes están codificados de una manera especial (ver más abajo)
  • Si el valor binario del componente OID tiene menos de 7 bits, la codificación es solo un octeto, manteniendo el valor del componente (nota, el bit más significativo, el extremo izquierdo, siempre será 0)
  • de lo contrario, si tiene 8 y más bits, el valor se "extiende" en varios octetos: divida la representación binaria en fragmentos de 7 bits (desde la derecha), el primer pad con ceros a la izquierda si es necesario, y forma octetos a partir de estos septetos agregando el bit 1 más significativo (izquierda), excepto el último fragmento, que tendrá el bit 0 allí.
  • los primeros dos componentes (XY) están codificados como si fueran un solo componente con un valor de 40 * X + Y

Esta es una nueva redacción de la recomendación X.690 del UIT-T, capítulo 8.19

Estoy teniendo problemas para entender los conceptos básicos de ASN.1.

Si un tipo es un OID, ¿el número correspondiente se codifica realmente en los datos binarios?

Por ejemplo en esta definición:

id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }

¿El 1.3.6.1.5.5.7.48.1 correspondiente se codifica en el binario exactamente así?

Lo pregunto porque estoy tratando de entender un valor específico que veo en un archivo DER (un certificado), que es 04020500, y no estoy seguro de cómo interpretarlo.


Esta es una implementación simplificada de Python 3 de la anterior, resp. una forma de cadena de un identificador de objeto en ASN.1 DER o BER.

def encode_variable_length_quantity(v:int) -> list: # Break it up in groups of 7 bits starting from the lowest significant bit # For all the other groups of 7 bits than lowest one, set the MSB to 1 m = 0x00 output = [] while v >= 0x80: output.insert(0, (v & 0x7f) | m) v = v >> 7 m = 0x80 output.insert(0, v | m) return output def encode_oid_string(oid_str:str) -> tuple: a = [int(x) for x in oid_str.split(''.'')] oid = [a[0]*40 + a[1]] # First two items are coded by a1*40+a2 # A rest is Variable-length_quantity for n in a[2:]: oid.extend(encode_variable_length_quantity(n)) oid.insert(0, len(oid)) # Add a Length oid.insert(0, 0x06) # Add a Type (0x06 for Object Identifier) return tuple(oid) if __name__ == ''__main__'': oid = encode_oid_string("1.2.840.10045.3.1.7") print(oid)


Sí, el OID está codificado en los datos binarios. El OID 1.3.6.1.5.5.7.48.1 que menciona se convierte en 2b 06 01 05 05 07 30 01 (los dos primeros números están codificados en un solo byte, todos los números restantes están codificados en un solo byte también porque son todos menor que 128).

Una buena descripción de la codificación OID se encuentra here .

Pero la mejor manera de analizar sus datos ASN.1 es pegarlos en un decodificador en línea, por ejemplo, http://lapo.it/asn1js/ .


Si todos tus dígitos son menores o iguales a 127, entonces tienes mucha suerte porque se pueden representar con un solo octeto cada uno. La parte difícil es cuando tienes números más grandes que son comunes, como 1.2.840.113549.1.1.5 (sha1WithRsaEncryption) . Estos ejemplos se centran en la decodificación, pero la codificación es todo lo contrario.

1. Los primeros dos ''dígitos'' están representados con un solo byte

Puedes decodificar leyendo el primer byte en un entero

var firstByteNumber = 42; var firstDigit = firstByteNumber / 40; var secondDigit = firstByteNumber % 40;

Produce los valores

1.2

2. Los bytes subsiguientes se representan utilizando la cantidad de longitud variable , también llamada base 128.

VLQ tiene dos formas,

Forma corta: si el octeto comienza con 0, entonces se representa simplemente usando los 7 bits restantes.

Forma larga: si el octeto comienza con un 1 (bit más significativo), combine los siguientes 7 bits de ese octeto más los 7 bits de cada octeto subsiguiente hasta que encuentre un octeto con un 0 como el bit más significativo (esto marca el último octeto).

El valor 840 se representaría con los siguientes dos bytes,

10000110 01001000 Combine to 00001101001000 and read as int.

Gran recurso para la codificación BER, http://luca.ntop.org/Teaching/Appunti/asn1.html

El primer octeto tiene valor 40 * valor1 + valor2. (Esto no es ambiguo, ya que value1 está limitado a los valores 0, 1 y 2; value2 está limitado al rango de 0 a 39 cuando value1 es 0 o 1; y, según X.208, n siempre es al menos 2.)

Los siguientes octetos, si los hay, codifican value3, ..., valuen. Cada valor se codifica en base 128, el dígito más significativo primero, con el menor número de dígitos posible, y el bit más significativo de cada octeto, excepto el último en la codificación del valor establecido en "1". Ejemplo: El primer octeto de la codificación BER del identificador de objeto de RSA Data Security, Inc. es 40 * 1 + 2 = 42 = 2a16. La codificación de 840 = 6 * 128 + 4816 es 86 48 y la codificación de 113549 = 6 * 1282 + 7716 * 128 + d16 es 86 f7 0d. Esto conduce a la siguiente codificación BER:

06 06 2a 86 48 86 f7 0d

Finalmente, aquí hay un decodificador OID que acabo de escribir en Perl.

sub getOid { my $bytes = shift; #first 2 nodes are ''special''; use integer; my $firstByte = shift @$bytes; my $number = unpack "C", $firstByte; my $nodeFirst = $number / 40; my $nodeSecond = $number % 40; my @oidDigits = ($nodeFirst, $nodeSecond); while (@$bytes) { my $num = convertFromVLQ($bytes); push @oidDigits, $num; } return join ''.'', @oidDigits; } sub convertFromVLQ { my $bytes = shift; my $firstByte = shift @$bytes; my $bitString = unpack "B*", $firstByte; my $firstBit = substr $bitString, 0, 1; my $remainingBits = substr $bitString, 1, 7; my $remainingByte = pack "B*", ''0'' . $remainingBits; my $remainingInt = unpack "C", $remainingByte; if ($firstBit eq ''0'') { return $remainingInt; } else { my $bitBuilder = $remainingBits; my $nextFirstBit = "1"; while ($nextFirstBit eq "1") { my $nextByte = shift @$bytes; my $nextBits = unpack "B*", $nextByte; $nextFirstBit = substr $nextBits, 0, 1; my $nextSevenBits = substr $nextBits, 1, 7; $bitBuilder .= $nextSevenBits; } my $MAX_BITS = 32; my $missingBits = $MAX_BITS - (length $bitBuilder); my $padding = 0 x $missingBits; $bitBuilder = $padding . $bitBuilder; my $finalByte = pack "B*", $bitBuilder; my $finalNumber = unpack "N", $finalByte; return $finalNumber; } }