Decodificar texto codificado con cryptlib usando Java (sin cryptlib)

Hola.

En primer lugar: soy nuevo en stackOverflow y en el tema del que estoy hablando ...
Estoy tratando de evitar el uso de cryptlib biblioteca para el cifrado TripleDES en mi aplicación Java (ahora estoy usando AES; para garantizar la compatibilidad con versiones anteriores, también quiero poder decodificar las cadenas que se crearon con la biblioteca cryptlib pero sin el uso de JNI).
Pero ninguna de las cosas que probé hasta ahora funcionó para mí.

Configuración:
Algoritmo: TripleDES
Modo: CBC
Formato: CRYPT_FORMAT_CRYPTLIB
La clave tiene un tamaño de 16 bytes (lo cual es inconveniente, pero BouncyCastle lo admitiría).
Y los datos cifrados tienen un tamaño que es no es un múltiplo de 8 (por ejemplo, 81 bytes).

En mi contenedor de la biblioteca (también en C), el contexto se crea de esta manera:

cryptCreateContext( &cryptContext, CRYPT_UNUSED, CRYPT_ALGO_3DES);
cryptSetAttribute( cryptContext, CRYPT_CTXINFO_MODE, CRYPT_MODE_CBC );
cryptSetAttributeString( cryptContext, CRYPT_CTXINFO_KEY, key, keyLen); 

El sobre se crea de esta manera:

cryptCreateEnvelope( envelope, CRYPT_FORMAT_CRYPTLIB );

Creo que el problema es el formato (ya que es un formato "nativo de cryptlib") - no puedo encontrar ninguna descripción al respecto en la red ...

Actualmente, mi mejor intento fue este:

Security.addProvider(new BouncyCastleProvider());
SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), "DESede");
IvParameterSpec iv = new IvParameterSpec(new byte[8]);
Cipher e_cipher = Cipher.getInstance("DESede/CBC/PKCS7Padding", "BC");
e_cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);

byte[] hexdecoded = Hex.decode(ENCRYPTED.getBytes());
byte [] cipherText = e_cipher.doFinal(hexdecoded);

return new String(cipherText);

También probé diferentes rellenos, pero siempre termino en una de estas dos Excepciones:

  • javax.crypto.IllegalBlockSizeException: los datos no están alineados con el tamaño del bloque
  • javax.crypto.IllegalBlockSizeException: último bloque incompleto en el descifrado

Me estoy desesperando un poco, así que estaría muy feliz si alguien aquí pudiera ayudarme ...

EDIT: Aquí está el código para el cifrado (CryptData.cpp):

bool CCryptData::encryptData(BYTE *key,int keyLen, BYTE *data, int dataLen, int resultMemSize, BYTE *result, int &resultLen)
{
    CRYPT_ENVELOPE cryptEnvelope;
    CRYPT_CONTEXT cryptContext;
    CRYPT_ALGO cryptAlgo = selectCipher( CRYPT_ALGO_3DES );
    int count;

    /* Create the session key context.  We don't check for errors here since
       this code will already have been tested earlier */
    cryptCreateContext( &cryptContext, CRYPT_UNUSED, cryptAlgo );
    cryptSetAttribute( cryptContext, CRYPT_CTXINFO_MODE, CRYPT_MODE_CBC );
    cryptSetAttributeString( cryptContext, CRYPT_CTXINFO_KEY, key, keyLen );

    /* Create the envelope, push in a password and the data, pop the
       enveloped result, and destroy the envelope */
    if( !createEnvelope( &cryptEnvelope ) || \
        !addEnvInfoNumeric( cryptEnvelope, CRYPT_ENVINFO_SESSIONKEY,
                            cryptContext ) )
        return( FALSE );

    cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_DATASIZE, dataLen );

    count = pushData( cryptEnvelope, data, dataLen, NULL, 0 );

    if( cryptStatusError( count ) )
        return( FALSE );

    resultLen = popData( cryptEnvelope, result, resultMemSize);

    if( cryptStatusError( count ) )
        return( FALSE );
    if( !destroyEnvelope( cryptEnvelope ) )
        return( FALSE );

    return true;
}

bool CCryptData::checkErrorStatus(int status, CString function)
{
    if( cryptStatusError( status ) )
    {
        m_lastError  = "Error occured in function " + function;
        m_lastError += " with StatusCode: " + status;
        m_bError = true;
        return true;
    }

    return false;
}

int CCryptData::createEnvelope( CRYPT_ENVELOPE *envelope )
{
    int status;

    /* Create the envelope */
    status = cryptCreateEnvelope( envelope, CRYPT_UNUSED, CRYPT_FORMAT_CRYPTLIB );
    if( checkErrorStatus(status, "createEnvelope"))
        return false;

    return( TRUE );
}

int CCryptData::destroyEnvelope( CRYPT_ENVELOPE envelope )
{
    int status;

    /* Destroy the envelope */
    status = cryptDestroyEnvelope( envelope );
    if( checkErrorStatus( status, "destroyEnvelope"))
        return false;

    return( TRUE );

}

int CCryptData::pushData( const CRYPT_ENVELOPE envelope, const BYTE *buffer,
                         const int length, const void *stringEnvInfo,
                         const int numericEnvInfo )
{
    int status, bytesIn;

    /* Push in the data */
    status = cryptPushData( envelope, buffer, length, &bytesIn );
    if( cryptStatusError( status ) )
    {
        printf( "cryptPushData() failed with error code %d, line %d.\n",
                status, __LINE__ );
        return( status );
    }
    if( bytesIn != length )
    {
        printf( "cryptPushData() only copied %d of %d bytes, line %d.\n",
                bytesIn, length, __LINE__ );
        return( SENTINEL );
    }

    /* Flush the data */
    status = cryptPushData( envelope, NULL, 0, NULL );
    if( cryptStatusError( status ) && status != CRYPT_ERROR_COMPLETE )
        {
        printf( "cryptPushData() (flush) failed with error code %d, line "
                "%d.\n", status, __LINE__ );
        return( status );
        }

    return( bytesIn );

}

int CCryptData::popData( CRYPT_ENVELOPE envelope, BYTE *buffer, int bufferSize )
{
    int status, bytesOut;

    status = cryptPopData( envelope, buffer, bufferSize, &bytesOut );
    if( cryptStatusError( status ) )
        {
        printf( "cryptPopData() failed with error code %d, line %d.\n",
                status, __LINE__ );
        return( status );
        }

    return( bytesOut );

}

int CCryptData::addEnvInfoNumeric( const CRYPT_ENVELOPE envelope,
                              const CRYPT_ATTRIBUTE_TYPE type,
                              const int envInfo )
{
    int status;

    status = cryptSetAttribute( envelope, type, envInfo );
    if( checkErrorStatus( status, "addEnvInfoNumeric"))
        return false;

    return( TRUE );

}

CRYPT_ALGO CCryptData::selectCipher( const CRYPT_ALGO algorithm )
{
    if( cryptStatusOK( cryptQueryCapability( algorithm, NULL ) ) )
        return( algorithm );
    return( CRYPT_ALGO_BLOWFISH );
}

EDIT 2: En comparación, es la misma implementación que se describe en esta lista de correo.
Pero quiero decodificarlo en Java ...

EDIT 3: Creo que el problema es que los datos cifrados están envueltos en cryptlib (como se ve en el código).

EDIT 4: Sé, sé que el problema es envolver. Cryptlib usa la clave de sesión del contexto de cripta creado para envolver los datos descifrados en el formato CRYPT_FORMAT_CRYPTLIB. La pregunta ahora es cómo decodificar el sobre antes de realizar el descifrado real.
¿Alguna sugerencia de cómo podría realizar la decodificación?

preguntado el 16 de mayo de 11 a las 16:05

2 Respuestas

Finalmente lo conseguí.
Después de depurar la fuente de cryptlib, descubrí que el texto cifrado es contenido envuelto en CMS codificado en HEX.
Usé este decodificador en línea para analizar la secuencia ASN.1 (CMS usa la notación ASN.1):
http://www.aggressivesoftware.com/tools/asn1decoder.php

Después de eso, pude reproducir la decodificación y descifrado en Java usando BouncyCastle como proveedor:


    public byte[] decrypt(final byte[] data) throws CryptoException {
        try {
            Security.addProvider(new BouncyCastleProvider());

        EncryptedContentInfo encryptionInfo = parseContentInfo(data);
        AlgorithmIdentifier algoID = encryptionInfo.getContentEncryptionAlgorithm();

        // get the real encrypted data
        byte[] encryptedData = encryptionInfo.getEncryptedContent().getOctets();

        // extract the initialization vector from the algorithm identifier object
        byte[] ivBytes = ((ASN1OctetString) algoID.getParameters()).getOctets();
        // create the key depending on the algorithm
        SecretKeySpec keySpec = new SecretKeySpec(rawKey, algoID.getObjectId().getId());
        // request cipher
        Cipher c = Cipher.getInstance(algoID.getObjectId().getId(), CRYPT_PROVIDER);

        c.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(ivBytes));
        byte[] decrypted = c.doFinal(encryptedData);

        return decrypted;

        } catch (NoSuchAlgorithmException e) {
            throw new CryptoException(e);
        } catch (NoSuchProviderException e) {
            throw new CryptoException(e);
        } catch (NoSuchPaddingException e) {
            throw new CryptoException(e);
        } catch (InvalidKeyException e) {
            throw new CryptoException(e);
        } catch (InvalidAlgorithmParameterException e) {
            throw new CryptoException(e);
        } catch (IllegalBlockSizeException e) {
            throw new CryptoException(e);
        } catch (BadPaddingException e) {
            throw new CryptoException(e);
        }
    }
}

private EncryptedContentInfo parseContentInfo(final byte[] encrypted) throws CryptoException { try { // create a new byte array stream ByteArrayInputStream bin = new ByteArrayInputStream(encrypted); // create an ASN.1 input stream ASN1InputStream ain = new ASN1InputStream(bin); // read the whole sequence ASN1Sequence mainSequence = (ASN1Sequence) ain.readObject(); // check if it is an encrypted data DERObjectIdentifier mainIdentifier = (DERObjectIdentifier) mainSequence .getObjectAt(ASN1IDENTIFIER_ID); if (!mainIdentifier.equals(OID_ENCRYPTED_DATA)) { throw new CryptoException("Given data is not encrypted CMS."); } // parse the encrypted object DERTaggedObject encryptedObject = (DERTaggedObject) mainSequence.getObjectAt(ASN1CONTENT_ID); // parse the sequence containing the useful informations ASN1Sequence encryptedSequence = (ASN1Sequence) encryptedObject.getObject(); // create the content info object EncryptedContentInfo info = EncryptedContentInfo.getInstance(encryptedSequence .getObjectAt(ASN1CONTENT_ID)); return info; } catch (IOException e) { // if the main sequence can not be read from the stream an IOException would be thrown throw new CryptoException(e); } catch (ClassCastException e) { // if the parsing fails, a ClassCastException would be thrown throw new CryptoException(e); } catch (IllegalStateException e) { // if the parsing fails, also a IllegalStateException can be thrown throw new CryptoException(e); } }

De esta manera pude decodificar el texto dado, identificar el algoritmo de descifrado real y extraer el vector de inicialización usado y los datos descifrados reales para realizar el descifrado.

contestado el 30 de mayo de 11 a las 12:05

Las excepciones parecen derivar directamente de la longitud del texto cifrado que está ingresando. Por definición, CBC genera un número entero de bloques, por lo que debería darnos un múltiplo de 8 ... La pregunta que estaría haciendo en este punto es: ¿Qué diablos está haciendo Cryptlib? ¿Puede mostrarnos el código cryptlib que está utilizando para crear la cadena cifrada?

contestado el 17 de mayo de 11 a las 02:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.