实战篇-OpenSSL之TripleDES加密算法-CBC模式

本文属于《OpenSSL加密算法库使用系列教程》之一,欢迎查看其它文章。

一、TripleDES简介

3DES又称Triple DES,是DES加密算法的一种模式,它使用2条不同的56位的密钥对数据进行三次加密。

数据加密标准(DES)是美国的一种由来已久的加密标准,它使用对称密钥加密法,并于1981年被ANSI组织规范为ANSI X.3.92。

DES使用56位密钥和密码块的方法,而在密码块的方法中,文本被分成64位大小的文本块然后再进行加密。比起最初的DES,3DES更为安全。

3DES(即Triple DES)是DES向AES过渡的加密算法,它使用3条56位的密钥对数据进行三次加密。是DES的一个更安全的变形。
它以DES为基本模块,通过组合分组方法设计出分组加密算法。比起最初的DES,3DES更为安全。

Triple DES属于对称加密算法,加解密使用同一个秘钥。

对称加密算法,一般有至少4种模式,即ECB、CBC、CFB、OFB等。

具体的加密原理,就不进行介绍了,本文主要从使用角度,进行说明。

以下命令行和编程实现,均基于OpenSSL开源库。在命令行中,我们可以使用命令实现对文件加解密,以验证我们的编程实现,是否正确。

二、CBC模式

加密块链模式 Cipher Block Chaining(CBC)。CBC 模式的加密首先也是将明文分成固定长度的块,然后将前面一个加密块输出的密文与下一个要加密的明文块进行异或操作,将计算结果再用密钥进行加密得到密 文。第一明文块加密的时候,因为前面没有加密的密文,所以需要一个初始化向量。跟 ECB 方式不一样,通过连接关系,使得密文跟明文不再是一一对应的关系,破解起来更困难,而且克服了只要简单调换密文块可能达到目的的攻击。

1、命令行操作

使用des-ede3-cbc对hello.txt加密,密钥为8cc72b05705d5c46f412af8cbed55aad8cc72b05705d5c46,初始化向量为667b02a85c61c786,密文为hello.en。

openssl enc -e -des-ede3-cbc -in hello.txt -out hello.en -K 8cc72b05705d5c46f412af8cbed55aad8cc72b05705d5c46 -iv 667b02a85c61c786

使用des-ede3-cbc对hello.en解密,密钥为8cc72b05705d5c46f412af8cbed55aad8cc72b05705d5c46,初始化向量为667b02a85c61c786,解密后的文件为hello.de。

openssl enc -d -des-ede3-cbc -in hello.en -out hello.de -K 8cc72b05705d5c46f412af8cbed55aad8cc72b05705d5c46 -iv 667b02a85c61c786

2、函数说明

CBC模式加密/解密:

void DES_ede3_cbc_encrypt(const unsigned char *input, unsigned char *output,
                          long length,
                          DES_key_schedule *ks1, DES_key_schedule *ks2,
                          DES_key_schedule *ks3, DES_cblock *ivec, int enc);
参数名称含义
input输入数据,长度任意
output输出数据,能够容纳下输入数据,且长度必须是8字节的倍数。
length输入数据的实际长度。
ks1使用DES_set_key_unchecked生成的Key
ks2使用DES_set_key_unchecked生成的Key
ks3使用DES_set_key_unchecked生成的Key
ivec可读写的一块内存。长度必须是8字节。
encDES_ENCRYPT代表加密, DES_DECRYPT代表解密

DES_ede3_cbc_encrypt在加密的过程中会修改ivec的内容,因此ivec参数不能是一个常量,而且不能在传递给加密函数后再立马传递给解密函数,必须重新赋值之后再传递给解密函数。

3、编程实现

(1)特别注意

CBC加解密函数DES_ede3_cbc_encrypt,可以对任意长度的输入数据进行加密,若输入数据不为8字节整数倍时,该函数内部会使用ZeroPadding自动对输入数据,进行填充,再进行加密。

ZeroPadding的填充方式:数据长度不对齐时使用0填充,否则不填充。

加密过程倒是没有什么问题,但是,在将不对齐的明文生成的密文,进行解密时,解密后的数据中末尾出现多个0数据,若明文数据中末尾本身带有0数据,则无法区分该0数据到底是填充的,还是真实数据。

故,建议将输入数据进行PKCS7填充后,再进行加密,以便解密时,正确还原明文数据。关于PKCS7填充,可参考《实战篇-OpenSSL之AES加密算法-ECB模式》中有介绍。

(2)实现CBC模式加解密

下面,函数已经封装完毕,如下:

/**
 * @brief TripleDES::cbc_encrypt
 * CBC模式加解密,支持对任意长度明文进行加解密。
 * @param in 输入数据
 * @param out 输出结果
 * @param key 密钥,长度必须是24字节,否则加密失败
 * @param ivec 初始向量,长度必须是8字节
 * @param enc true-加密,false-解密
 */
void TripleDES::cbc_encrypt(const QByteArray &in, QByteArray &out, const QByteArray &key, const QByteArray &ivec, bool enc)
{
    // 检查密钥合法性(只能是24字节)
    Q_ASSERT(key.size() == 24);
    Q_ASSERT(ivec.size() == 8); // 初始向量为8字节

    // 设置key
    DES_key_schedule sch1, sch2, sch3;
    setKey(key, sch1, sch2, sch3);

    // 进行加解密
    QByteArray ivecTemp = ivec; // 此参数被自动修改,故使用局部变量传递
    if (enc)
    {
        // 进行PKCS7Padding填充
        QByteArray inTemp = Padding::PKCS7Padding(in, DES_BLOCK_SIZE);

        // 执行加密
        out.resize(inTemp.size()); // 调整输出buf大小
        DES_ede3_cbc_encrypt((const unsigned char *)inTemp.constData(),
                             (unsigned char *)out.data(),
                             inTemp.size(), &sch1, &sch2, &sch3,
                             (DES_cblock *)ivecTemp.data(), DES_ENCRYPT);
    }
    else
    {
        // 执行解密
        out.resize(in.size()); // 调整输出buf大小
        DES_ede3_cbc_encrypt((const unsigned char *)in.constData(),
                             (unsigned char *)out.data(),
                             in.size(), &sch1, &sch2, &sch3,
                             (DES_cblock *)ivecTemp.data(), DES_DECRYPT);

        // 解除PKCS7Padding填充
        out = Padding::PKCS7UnPadding(out);
    }
}

加解密过程与ECB模式类似。

加密过程:

  • 生成key
  • 进行PKCS7Padding填充
  • 执行加密

解密过程:

  • 生成key
  • 执行解密
  • 去除PKCS7Padding填充

经测试,本函数支持对任意长度输入数据进行加解密。

(3)测试代码

void createTestData(QByteArray& data, int size)
{
    data.resize(size);
    for (int i = 0; i < size; i++)
    {
        data[i] = i % 128;
    }
}

void testTripleDES(const QByteArray& data)
{
    QByteArray plainText = data;
    QByteArray encryptText;
    QByteArray decryptText;

    QByteArray key = QByteArray::fromHex("8cc72b05705d5c46f412af8cbed55aad8cc72b05705d5c46");
    QByteArray ivec = QByteArray::fromHex("667b02a85c61c786");

    // TripleDES cbc模式加密验证
    TripleDES tripleDES;
    tripleDES.cbc_encrypt(plainText, encryptText, key, ivec, true);     // 加密
    tripleDES.cbc_encrypt(encryptText, decryptText, key, ivec, false);  // 解密
    qDebug() << "TripleDES cbc encrypt verify" << ((decryptText == plainText) ? "succeeded" : "failed");
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 产生1MB+3B的测试数据,为了使该测试数据长度,不为8或16的整数倍
    QByteArray data;
    createTestData(data, 1*1024*1024+3);

    // 测试TripleDES
    testTripleDES(data);

    return a.exec();
}

执行结果:

在这里插入图片描述

本文涉及工程代码地址:https://gitee.com/bailiyang/cdemo/tree/master/Qt/49OpenSSL/OpenSSL



若对你有帮助,欢迎点赞、收藏、评论,你的支持就是我的最大动力!!!

同时,阿超为大家准备了丰富的学习资料,欢迎关注公众号“超哥学编程”,即可领取。

在这里插入图片描述

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要实现 Flutter App 和 PHP 服务器之间的通信,可以使用 HTTP 协议和 POST 请求,然后在 PHP 服务器端使用 AES-256-CBC 加密算法对请求的数据进行加密,再将加密后的数据返回给 Flutter App。Flutter App 在收到数据后,再使用相同的 AES-256-CBC 加密算法对数据进行解密。 以下是一个示例代码,用于在 Flutter App 中发送加密后的数据到 PHP 服务器: ```dart import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:crypto/crypto.dart'; import 'package:pointycastle/block/aes.dart'; import 'package:pointycastle/block/modes/cbc.dart'; import 'package:pointycastle/padded_block_cipher/padded_block_cipher_impl.dart'; import 'dart:math'; import 'dart:typed_data'; Future<String> sendEncryptedData(String data) async { // Generate a random initialization vector final random = Random.secure(); final iv = Uint8List.fromList(List.generate(16, (_) => random.nextInt(256))); // Encrypt the data using AES-256-CBC final key = utf8.encode('your_secret_key_here'); final cipher = PaddedBlockCipherImpl( PKCS7Padding(), CBCBlockCipher(AESFastEngine()), ); cipher.init(true, PaddedBlockCipherParameters<CipherParameters, CipherParameters>( ParametersWithIV<KeyParameter>(KeyParameter(key), iv), null, )); final encryptedData = cipher.process(utf8.encode(data)); // Send the encrypted data and initialization vector to the PHP server final response = await http.post( Uri.parse('http://your_php_server_url_here'), body: { 'data': base64.encode(encryptedData), 'iv': base64.encode(iv), }, ); // Return the response from the PHP server return response.body; } ``` 在 PHP 服务器端,可以使用以下代码对请求的数据进行解密: ```php <?php // Get the encrypted data and initialization vector from the POST request $data = base64_decode($_POST['data']); $iv = base64_decode($_POST['iv']); // Decrypt the data using AES-256-CBC $key = 'your_secret_key_here'; $decryptedData = openssl_decrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv); // Return the decrypted data to the Flutter App echo $decryptedData; ?> ``` 需要注意的是,为了确保数据的安全性,密钥应该保密,并且只有 Flutter App 和 PHP 服务器知道。另外,初始化向量应该是随机生成的,并且每次加密的时候都应该使用不同的初始化向量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百里杨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值