¿Cómo descifrar manualmente un mensaje SOAP usando openssl?

3

Tengo un mensaje SOAP como este (los datos clave y los datos cifrados se han truncado):



<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  <s:Header>
    <o:Security xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">
      <e:EncryptedKey xmlns:e="http://www.w3.org/2001/04/xmlenc#" Id="_0">
        <e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
        <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
          <o:SecurityTokenReference>
            <X509Data>
              <X509IssuerSerial>
                <X509IssuerName>CN=blah, O=blah, L=blah, S=blah, C=blah</X509IssuerName>
                <X509SerialNumber>1</X509SerialNumber>
              </X509IssuerSerial>
            </X509Data>
          </o:SecurityTokenReference>
        </KeyInfo>
        <e:CipherData>
          <e:CipherValue>TiMPCLfQgfw==</e:CipherValue>
        </e:CipherData>
        <e:ReferenceList>
          <e:DataReference URI="#_2"/>
        </e:ReferenceList>
      </e:EncryptedKey>
    </o:Security>
  </s:Header>
  <s:Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" u:Id="_1">
    <e:EncryptedData xmlns:e="http://www.w3.org/2001/04/xmlenc#" Id="_2" Type="http://www.w3.org/2001/04/xmlenc#Content">
      <e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
      <e:CipherData>
        <e:CipherValue>1qsIPulqkVQ3==</e:CipherValue>
      </e:CipherData>
    </e:EncryptedData>
  </s:Body>
</s:Envelope>

El cuerpo se ha codificado en aes-256-cbc y luego la clave de sesión que hizo esta codificación se codificó con mi clave pública.

Mi pregunta es ¿cómo decodifico esto manualmente?

Estaba intentando lo siguiente:

  1. Copiar clave de sesión cifrada en un archivo

      

    echo "TiMPCLfQgfw==" > sessionkey.enc

  2. Formato de clave a 64 caracteres como máximo por línea:

      

    sed -e "s/.{64}/&\n/g" < sessionkey.enc > sessionkey.hex

  3. Convierta la clave de sesión a formato binario para openssl (ya que el comando rsautl solo funciona con binario):

      

    openssl enc -in sessionkey.hex -out sessionkey.bin -d -a

  4. Descifre la clave de sesión con openssl y privatekey:

      

    openssl rsautl -decrypt -in sessionkey.bin -out sessionkey.dec -inkey myprivatekey.key

  5. Copiar el cuerpo del mensaje cifrado en un archivo

      

    echo "1qsIPulqkVQ3==" > messagebody.enc

  6. Formato del cuerpo del mensaje cifrado a 64 caracteres como máximo por línea (formato hexadecimal):

      

    sed -e "s/.{64}/&\n/g" < messagebody.enc > messagebody.hex

  7. Convertir la clave del cuerpo del mensaje a formato binario para openssl:

      

    openssl enc -in messagebody.hex -out messagebody.bin -d -a

  8. Descifre el cuerpo del mensaje con openssl y la clave de sesión:

      

    openssl enc -aes-256-cbc -d -in messagebody.bin -out messagebody.dec -kfile sessionkey.dec

Pero cuando intento esto, obtengo un "número mágico malo" en este último paso. ¿Alguna idea de por qué?

    
pregunta Barry Pollard 17.04.2013 - 14:38
fuente

2 respuestas

1

Bien, finalmente lo descubrí yo mismo después de muchas pruebas y errores.

Los pasos son:

  1. Copie la clave de sesión cifrada en un archivo y la base64 la descodifique al mismo tiempo

echo "TiMPCLfQgfw==" | base64 -d -i > sessionkey.enc

  1. Descifra la clave de sesión con openssl y privatekey:

openssl rsautl -decrypt -in sessionkey.enc -out sessionkey.dec -inkey myprivatekey.key

  1. Copie la clave de sesión cifrada en un archivo y la base64 la descodifique al mismo tiempo

echo "1qsIPulqkVQ3==" | base64 -d -i > body.enc

  1. Lee tu clave de sesión en hexadecimal (estoy seguro de que hay una forma más fácil de hacer esto, pero aquí hay una forma de usar el comando hexdump unix):

hexdump -C sessionkey.dec

Y digamos que se ve así (esta es una tecla demasiado corta para aes256 pero es solo para fines de demostración):

00000000  8a bc f2 86 25 4c 76 c4                           |....%Lv.|
00000008
  1. Lee tu cuerpo en hexadecimal (de nuevo, debes escribirlo):

hexdump -C body.enc

y digamos que se ve así:

00000000  80 33 28 0f 7a fa 73 f3  7e 4a 93 e6 a7 5f fe 2d  |.3(.z.s.~J..._.-|
00000010  29 88 84 4e e5 23 33 b8  0e 1a 43 fb 43 b1 83 ce  |)..N.#3...C.C...|
00000020

El primer bloque (80 - f3) es el cifrado iv

  1. Descifre el mensaje utilizando la versión hexadecimal de la clave y la versión hexadecimal del iv:

openssl enc -d -aes-256-cbc -in body.enc -K 8abcf286254c76c4 -iv 8033280f7afa73f3 -out body.dec

Tenga en cuenta que obtendrá algo de basura al principio, ya que debería quitar el iv del principio del cuerpo antes de descifrarlo.

No es bonito, y definitivamente se puede mejorar masivamente con algunas secuencias de comandos, pero esto te da la idea de qué hacer.

    
respondido por el Barry Pollard 19.04.2013 - 18:55
fuente
4

(No es "hex", sino Base64 . No es un punto crítico aquí, pero si obtiene la terminología en ese momento es más fácil buscar soluciones en la Web.)

Al realizar el cifrado simétrico, openssl enc , la herramienta de línea de comandos, utiliza su propio formato no estándar, específico de OpenSSL, que no coincide con el uso de SOAP. OpenSSL agrega un encabezado específico (ese es el "número mágico" del que se queja) y algo de aleatoriedad (una "sal": las sales tienen mucho sentido cuando las claves son contraseñas, pero ese no es el caso aquí). Según la especificación , cifrado con AES-256 -CBC en SOAP simplemente agrega el IV como los primeros 16 bytes, y no hay encabezado ni sal. Esto significa que el siguiente debería funcionar (no lo he intentado):

  1. Extraer el IV:

    dd if=messagebody.bin of=iv.bin bs=16 count=1

  2. Convierta el IV a hexadecimal (hexadecimal real esta vez, no Base64):

    ivhex=$(od -t x1 -w iv.bin | sed 's/^[0-9A-Fa-f]*//' | sed 's/ //g')

  3. Extraiga los datos cifrados reales:

    dd if=messagebody.in of=edata.bin bs=16 skip=1

  4. Aplique el descifrado con IV explícito e instruya a OpenSSL no para que juegue sus propios juegos no estándar con sales:

    openssl enc -aes-256-cbc -iv $ivhex -kfile sessionkey.dec -nosalt -in edata.bin -out cdata.bin

El último problema espinoso restante es el relleno . OpenSSL, aparentemente, utiliza el relleno PKCS # 7, lo que significa que los bytes n se agregan a los datos en el cifrado, de modo que:

  • n está entre 1 y 16 (al menos uno, como máximo dieciséis bytes agregados);
  • la longitud total rellenada es un múltiplo de 16;
  • los bytes agregados n tienen un valor n .

SOAP, por otro lado, exige algo similar, excepto que solo requieren que el último byte agregado tenga un valor n ; Los n-1 otros bytes pueden tener cualquier valor. Por lo tanto, cuando OpenSSL descifre los datos, podría encontrar un relleno que no es exactamente lo que esperaría (depende de si OpenSSL insiste o no en todos los bytes de relleno que tienen un valor n , y en qué valores de byte de relleno fueron utilizados por quien produjo los datos cifrados en primer lugar).

Si el relleno parece ser un problema, intente agregar la opción -nopad al comando openssl enc ; luego tendrá que inspeccionar los datos binarios obtenidos (por ejemplo, con od ) y eliminar los bytes de relleno usted mismo (nuevamente, con dd ).

(Todo lo anterior se ha escrito asumiendo un sistema similar a Unix, por ejemplo, Linux o FreeBSD.)

    
respondido por el Tom Leek 19.04.2013 - 18:57
fuente

Lea otras preguntas en las etiquetas