3


I need to implement in Crypto++ the equivalent to BouncyCastle's "ECIESwithAES-CBC/NONE/PKCS7Padding".

The main reason is that I need to encrypt data on iOS and decrypt it on the backend with BouncyCastle in Java and we want to use those specific algorithms/configurations.

I have zero experience with C++, but here is what I've got so far in Crypto++:

// loaded private key
const unsigned char* privateKey;
size_t keyLength;

AutoSeededRandomPool prng;

ECIES_BC<ECP>::Decryptor decryptor;
decryptor.AccessPrivateKey().Load(StringStore(privateKey, keyLength).Ref());
ECIES_BC<ECP>::Encryptor encryptor(decryptor);

std::string plain("a"); // the message
std::string cipher;

SecByteBlock key(AES::DEFAULT_KEYLENGTH);
prng.GenerateBlock( key, key.size() );

byte iv[ AES::BLOCKSIZE ];
prng.GenerateBlock( iv, sizeof(iv) );

CBC_Mode< AES >::Encryption e;
e.SetKeyWithIV( key, key.size(), iv );

StringSource ss1( plain, true,
        new StreamTransformationFilter( e,
                new StringSink( cipher ), StreamTransformationFilter::PKCS_PADDING
                                      ) // StreamTransformationFilter
                 ); // StringSource


std::string cryptogram;
StringSource ss2 (cipher, true,
                            new PK_EncryptorFilter(prng, encryptor, new StringSink(cryptogram) ) );
// ... decrypt cryptogram in bouncy castle

Here is the java part:

private static final Provider SECURITY_PROVIDER = new BouncyCastleProvider();

public byte[] decryptMessage(byte[] message) throws Exception {
    KeyFactory keyFactory = KeyFactory.getInstance("EC", SECURITY_PROVIDER);
    PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(
            IOUtils.toByteArray(getClass().getResourceAsStream("/key.pkcs8")));
    PrivateKey privKey = keyFactory.generatePrivate(privSpec);

    Cipher cipher = Cipher.getInstance("ECIESwithAES-CBC/NONE/PKCS5Padding", SECURITY_PROVIDER);
    cipher.init(Cipher.DECRYPT_MODE, privKey);
    byte[] result = cipher.doFinal(message);
    return result;
}

Currently when I take the output from Crypto++ and try to decrypt it in BouncyCastle, it throws an exception:

javax.crypto.BadPaddingException: pad block corrupted
  at org.bouncycastle.jcajce.provider.asymmetric.ec.IESCipher.engineDoFinal(Unknown Source)
  at javax.crypto.Cipher.doFinal(Cipher.java:2087)
  ...

I'm not sure if it is really a padding issue or am I doing something completely wrong?

Any suggestions and help would be greatly appreciated.
Thank you!

PS: I've already applied the bouncy castle patch mentioned at CryptoWiki

1 Answers1

3

ECIES has a few different encryption methods, depending on the standard being used. At the moment, Crypto++ only implements the P1363 XOR method (below is from gfpcrypt.h). That probably explains most of the exception.

In order to fix things, I believe you have three choices. First, you can use the XOR method since both Bouncy Castle and Crypto++ have it.

Second, you may be able to use Jack Lloyd's Botan. Both Botan and Crypto++ attempt to align with Bouncy Castle in an effort to promote interop, but Botan has a few more encryption methods.

Third, Crypto++ needs to add another encryption method compatible with Bouncy Castle. I suppose it would be called DL_EncryptionAlgorithm_AES_CBC. I'm not sure what the None refers to in AES-CBC/NONE/PKCS7Padding.

Crypto++ would be happy to add DL_EncryptionAlgorithm_AES_CBC. To add it, I need someone to work with who has Java/BC experience. If you are interested, contact me a noloader, gmail account.


Regarding "Botan and Crypto++ attempt to align with Bouncy Castle in an effort to promote interop" ... Things are a mess for users. Martínez, Encinas, and Ávila note in A Survey of the Elliptic Curve Integrated Encryption Scheme:

... it is not possible to implement a software version compatible with all those standards, regarding both the specific operations and the list of allowed functions and algorithms.

I can point out countless examples of interop problems, from your question to problems with Bitcoin and Zcash standardizing their protocol because of too many incompatible choices. It goes on and on.


These comments are from gfpcrypt.h, and available in the Crypto++ Manual for ECIES:

//! \class DL_EncryptionAlgorithm_Xor
//! \brief P1363 based XOR Encryption Method
//! \tparam MAC MessageAuthenticationCode derived class used for MAC computation
//! \tparam DHAES_MODE flag indicating DHAES mode
//! \tparam LABEL_OCTETS flag indicating the label is octet count
//! \details DL_EncryptionAlgorithm_Xor is based on an early P1363 draft, which itself appears to be based on an
//!   early Certicom SEC-1 draft (or an early SEC-1 draft was based on a P1363 draft). Crypto++ 4.2 used it in its Integrated
//!   Ecryption Schemes with <tt>NoCofactorMultiplication</tt>, <tt>DHAES_MODE=false</tt> and <tt>LABEL_OCTETS=true</tt>.
//! \details If you need this method for Crypto++ 4.2 compatibility, then use the ECIES template class with
//!   <tt>NoCofactorMultiplication</tt>, <tt>DHAES_MODE=false</tt> and <tt>LABEL_OCTETS=true</tt>.
//! \details If you need this method for Bouncy Castle 1.54 and Botan 1.11 compatibility, then use the ECIES template class with
//!   <tt>NoCofactorMultiplication</tt>, <tt>DHAES_MODE=ture</tt> and <tt>LABEL_OCTETS=false</tt>.
//! \details Bouncy Castle 1.54 and Botan 1.11 compatibility are the default template parameters.
//! \since Crypto++ 4.0
template <class MAC, bool DHAES_MODE, bool LABEL_OCTETS=false>
class DL_EncryptionAlgorithm_Xor : public DL_SymmetricEncryptionAlgorithm
{
public:

    bool ParameterSupported(const char *name) const {return strcmp(name, Name::EncodingParameters()) == 0;}
    size_t GetSymmetricKeyLength(size_t plaintextLength) const
        {return plaintextLength + static_cast<size_t>(MAC::DIGESTSIZE);}
    size_t GetSymmetricCiphertextLength(size_t plaintextLength) const
        {return plaintextLength + static_cast<size_t>(MAC::DIGESTSIZE);}
    size_t GetMaxSymmetricPlaintextLength(size_t ciphertextLength) const
        {return SaturatingSubtract(ciphertextLength, static_cast<size_t>(MAC::DIGESTSIZE));}
    void SymmetricEncrypt(RandomNumberGenerator &rng, const byte *key, const byte *plaintext, size_t plaintextLength, byte *ciphertext, const NameValuePairs &parameters) const
    {
        CRYPTOPP_UNUSED(rng);
        const byte *cipherKey = NULL, *macKey = NULL;
        if (DHAES_MODE)
        {
            macKey = key;
            cipherKey = key + MAC::DEFAULT_KEYLENGTH;
        }
        else
        {
            cipherKey = key;
            macKey = key + plaintextLength;
        }

        ConstByteArrayParameter encodingParameters;
        parameters.GetValue(Name::EncodingParameters(), encodingParameters);

        if (plaintextLength)    // Coverity finding
            xorbuf(ciphertext, plaintext, cipherKey, plaintextLength);

        MAC mac(macKey);
        mac.Update(ciphertext, plaintextLength);
        mac.Update(encodingParameters.begin(), encodingParameters.size());
        if (DHAES_MODE)
        {
            byte L[8];
            PutWord(false, BIG_ENDIAN_ORDER, L, (LABEL_OCTETS ? word64(encodingParameters.size()) : 8 * word64(encodingParameters.size())));
            mac.Update(L, 8);
        }
        mac.Final(ciphertext + plaintextLength);
    }
    DecodingResult SymmetricDecrypt(const byte *key, const byte *ciphertext, size_t ciphertextLength, byte *plaintext, const NameValuePairs &parameters) const
    {
        size_t plaintextLength = GetMaxSymmetricPlaintextLength(ciphertextLength);
        const byte *cipherKey, *macKey;
        if (DHAES_MODE)
        {
            macKey = key;
            cipherKey = key + MAC::DEFAULT_KEYLENGTH;
        }
        else
        {
            cipherKey = key;
            macKey = key + plaintextLength;
        }

        ConstByteArrayParameter encodingParameters;
        parameters.GetValue(Name::EncodingParameters(), encodingParameters);

        MAC mac(macKey);
        mac.Update(ciphertext, plaintextLength);
        mac.Update(encodingParameters.begin(), encodingParameters.size());
        if (DHAES_MODE)
        {
            byte L[8];
            PutWord(false, BIG_ENDIAN_ORDER, L, (LABEL_OCTETS ? word64(encodingParameters.size()) : 8 * word64(encodingParameters.size())));
            mac.Update(L, 8);
        }
        if (!mac.Verify(ciphertext + plaintextLength))
            return DecodingResult();

        if (plaintextLength)    // Coverity finding
            xorbuf(plaintext, ciphertext, cipherKey, plaintextLength);

        return DecodingResult(plaintextLength);
    }
};
jww
  • 83,594
  • 69
  • 338
  • 732