使用OpenSSL

时间:2015-10-08 13:30:44

标签: c macos encryption openssl aes

我正在研究C中的一个跨平台(Windows和Mac OS X)代码,它需要使用带有CBC的AES-256和128位的块大小来加密/解密blob。在各种库和API中,我选择了OpenSSL。

然后,这段代码将使用多部分形式的PUT将blob上传到服务器,然后服务器使用.NET的加密框架(Aes,CryptoStream等)中的相同设置对其进行解密。

我面临的问题是,当在Windows上进行本地加密时,服务器解密工作正常,但在Mac OS X上进行加密时服务器解密失败 - 服务器抛出“填充无效且无法删除异常” ”

我从很多角度看待这个:

  1. 我确认传输正确 - 服务器的解密方法收到的字节数组与从Mac OS X和Windows发送的字节数组完全相同
  2. 对于相同的密钥,加密blob的实际内容在Windows和Mac OS X之间是不同的。我使用硬编码密钥对其进行了测试,并在Windows和Mac OS X上针对相同的blob运行此修补程序
  3. 我确定填充正确,因为它由OpenSSL处理,因为相同的代码适用于Windows。即便如此,我尝试实现填充方案,因为它是微软的.NET参考源,但仍然没有去
  4. 我确认对于Windows和Mac OS X,IV是相同的(我想也许有一些特殊字符出现问题,例如出现在IV中的ETB,但是没有)
  5. 我尝试过LibreSSL和mbedtls,没有任何积极的结果。在mbedtls中我还必须实现填充,因为据我所知,填充是API用户的责任
  6. 我已经遇到这个问题差不多两个星期了,我开始把我的(稀少的)头发拉出来
  7. 作为参考框架,我将发布C客户端的加密代码和服务器的C#代码进行解密。服务器端的一些小细节将被省略(它们不会干扰加密代码)。

    客户端:

    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    void
    __setup_aes(EVP_CIPHER_CTX *ctx, const char *key, qvr_bool encrypt)
    {
        static const char *iv = ""; /* for security reasons, the actual IV is omitted... */
    
        if (encrypt)
            EVP_EncryptInit(ctx, EVP_aes_256_cbc(), key, iv);
        else
            EVP_DecryptInit(ctx, EVP_aes_256_cbc(), key, iv);
    }
    
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    void
    __encrypt(void *buf,
        size_t buflen,
        const char *key,
        unsigned char **outbuf,
        size_t *outlen)
    {
        EVP_CIPHER_CTX ctx;
        int blocklen = 0;
        int finallen = 0;
        int remainder = 0;
    
        __setup_aes(&ctx, key, QVR_TRUE);
    
        EVP_CIPHER *c = ctx.cipher;
        blocklen = EVP_CIPHER_CTX_block_size(&ctx);
    
        //*outbuf = (unsigned char *) malloc((buflen + blocklen - 1) / blocklen * blocklen);
        remainder = buflen % blocklen;
        *outlen = remainder == 0 ? buflen : buflen + blocklen - remainder;
        *outbuf = (unsigned char *) calloc(*outlen, sizeof(unsigned char));
    
        EVP_EncryptUpdate(&ctx, *outbuf, outlen, buf, buflen);
        EVP_EncryptFinal_ex(&ctx, *outbuf + *outlen, &finallen);
    
        EVP_CIPHER_CTX_cleanup(&ctx);
        //*outlen += finallen;
    }
    

    服务器

    static Byte[] Decrypt(byte[] input, byte[] key, byte[] iv)
        {
            try
            {
                // Check arguments.
                if (input == null || input.Length <= 0)
                    throw new ArgumentNullException("input");
                if (key == null || key.Length <= 0)
                    throw new ArgumentNullException("key");
                if (iv == null || iv.Length <= 0)
                    throw new ArgumentNullException("iv");
    
                byte[] unprotected;
    
    
                using (var encryptor = Aes.Create())
                {
                    encryptor.Key = key;
                    encryptor.IV = iv;
                    using (var msInput = new MemoryStream(input))
                    {
                        msInput.Position = 0;
                        using (
                            var cs = new CryptoStream(msInput, encryptor.CreateDecryptor(),
                                CryptoStreamMode.Read))
                        using (var data = new BinaryReader(cs))
                        using (var outStream = new MemoryStream())
                        {
                            byte[] buf = new byte[2048];
                            int bytes = 0;
                            while ((bytes = data.Read(buf, 0, buf.Length)) != 0)
                                outStream.Write(buf, 0, bytes);
    
                            return outStream.ToArray();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
    
        }
    

    有没有人知道为什么会发生这种情况?作为参考,这是来自Microsoft的参考源.sln的.NET方法(我认为)解密:https://gist.github.com/Metaluim/fcf9a4f1012fdeb2a44f#file-rijndaelmanagedtransform-cs

2 个答案:

答案 0 :(得分:1)

OpenSSL版本的差异是混乱的。首先,我建议您明确规定双方的密钥长度,密钥,IV和加密模式。我没有在代码中看到。然后,我建议您在服务器端解密而不填充。这将始终成功,然后您可以检查最后一个块是否符合您的期望。

使用Windows-Encryption和MacOS-Encryption变体执行此操作,您将看到差异,很可能是在填充上。

C ++代码中的外部填充看起来很奇怪。加密16字节长的明文会得到32字节的密文,但是您只提供16字节长的缓冲区。这是行不通的。您将超出范围。由于内存布局更为宽大,也许它在Windows上只是偶然地工作,而在MacOS上失败。

答案 1 :(得分:0)

我认为AES填充方案在OpenSSL版本0.9.8 *和1.0.1 *之间已经改变(至少在0.9.8r和1.0.1j之间)。如果您的两个模块使用这些不同版本的OpenSSL,那么这可能是您遇到问题的原因。要验证这一点,请先检查OpenSSL版本。如果按照描述的情况进行操作,您可以考虑将填充方案对齐为相同。