使用Java解码cryptlib编码的文本(没有cryptlib)

时间:2011-05-16 16:49:47

标签: java cryptography bouncycastle 3des cryptlib

你好。

首先:我是stackOverflow的新手和我正在谈论的主题...
我试图避免在我的Java应用程序中使用cryptlib库进行TripleDES加密(现在我正在使用AES - 以确保向下兼容性我也希望能够解码使用cryptlib创建的字符串图书馆,但没有使用JNI) 但是我现在尝试的任何事情都没有为我工作。

配置:
算法: TripleDES
模式: CBC
格式: CRYPT_FORMAT_CRYPTLIB
密钥的大小为16字节(这很不方便,但BouncyCastle会支持它) 并且加密数据的大小是8的倍数(例如,81字节)。

在我的库包装器中(也在C中),以这种方式创建上下文:

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

以这种方式创建信封:

cryptCreateEnvelope( envelope, CRYPT_FORMAT_CRYPTLIB );

我认为问题是格式(因为它是“cryptlib原生”格式) - 我在网上找不到任何关于它的描述......

目前我最好的尝试就是这个:

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);

我也尝试了不同的填充,但我总是以这两种异常中的一种结束:

  • javax.crypto.IllegalBlockSizeException: 数据不是块大小对齐
  • javax.crypto.IllegalBlockSizeException: 最后一个块在解密中不完整

我有点绝望,所以如果有人能帮助我,我会很高兴...

修改 以下是加密代码(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 );
}

编辑2: 在比较中,它与描述in this mailing list的实现相同 但我想用Java解码它......

编辑3: 我认为问题是,加密数据被封装在cryptlib中(如代码所示)。

编辑4: 我知道问题在于包络。 cryptlib使用创建的crypt上下文的会话密钥以CRYPT_FORMAT_CRYPTLIB格式封装解密数据。 现在的问题是如何在执行真正的解密之前解码信封 有什么建议我可以执行解码吗?

2 个答案:

答案 0 :(得分:1)

我终于明白了 在调试了cryptlib的源代码后,我发现加密文本是HEX编码的CMS封装内容 我使用这个在线解码器来分析ASN.1序列(CMS使用ASN.1表示法):
http://www.aggressivesoftware.com/tools/asn1decoder.php

之后,我可以使用BouncyCastle作为提供程序在Java中重现解码和解密:


    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);
        }
    }
}

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); } } }

通过这种方式,我能够解码给定的文本,识别真正的解密算法并提取使用的初始化向量和真实的解密数据来执行解密。

答案 1 :(得分:0)

例外情况似乎直接来自您输入的密文长度。根据定义,CBC会输出一定数量的块,因此它应该给我们一个8的倍数......我要问的问题是重点是,cryptlib到底在做什么?你能告诉我们你用来创建加密字符串的cryptlib代码吗?