【问题标题】:InvalidCiphertext exception when decrypting ciphertext解密密文时出现InvalidCiphertext异常
【发布时间】:2015-01-24 16:06:29
【问题描述】:

我正在研究一种用于安全通信的新协议,但我在解密密文时遇到了问题。

数据包保存在 uint8_t* 变量中并加密。直到这部分一切顺利。但是当我尝试解密时,我遇到了以下问题:

1) 如果我发送向量和大小(实际上是 20 但我只想解密最后 16 个字节):

CBC_Mode< AES >::Decryption decryptor;
decryptor.SetKeyWithIV( key, CryptoPP::AES::DEFAULT_KEYLENGTH, iv );

CryptoPP::StringSource ss( vector+4, 16 , true,
        new CryptoPP::StreamTransformationFilter( decryptor,
             new CryptoPP::StringSink( decryptedtext ) ) );

我明白了:

terminate called after throwing an instance of 'CryptoPP::InvalidCiphertext'
  what():  StreamTransformationFilter: invalid PKCS #7 block padding found

2)如果我只是发送没有大小的矢量:

CryptoPP::StringSource ss( vector+4, true,
       new CryptoPP::StreamTransformationFilter( decryptor,
              new CryptoPP::StringSink( decryptedtext ) ) );

程序运行,但我得到的都是 00:

Text Encrypted (20 bytes)
8c 97 b7 d8 74 80 3d 9f 9f 62 2e 93 38 c7 d1 b de a4 21 80 

Text Decrypted (16 bytes)
0 0 0 0 0 0 0 0 68 0 0 0 0 0 0 0 0 0 0 0 

我读到可能是密钥生成不正确,但我正在使用 16 的大小,我是这样做的:

 byte key[ CryptoPP::AES::DEFAULT_KEYLENGTH ], iv[      CryptoPP::AES::BLOCKSIZE ];
 memset( key, 0x00, CryptoPP::AES::DEFAULT_KEYLENGTH );
 memset( iv, 0x00, CryptoPP::AES::BLOCKSIZE );

3) 我还尝试将向量转换为 char 并像字符串一样发送:

CryptoPP::StringSource ss( reinterpret_cast<const unsigned char*>( (vector + 4) ), 16, true,
        new CryptoPP::StreamTransformationFilter( decryptor,
            new CryptoPP::StringSink( decryptedtext ) ) );

但我又得到了同样的东西:

terminate called after throwing an instance of 'CryptoPP::InvalidCiphertext'
  what():  StreamTransformationFilter: invalid PKCS #7 block padding found

请帮忙,我已经尝试了几天来找出问题所在。这花了我太长时间,我找不到解决方案。

有人知道会发生什么吗?

如果您需要更多详细信息、代码或任何内容,请告诉我。

编辑:

4) 我尝试的另一件事是(构造解密器的另一种方法):

CryptoPP::AES::Decryption aesDecryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH);
CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption( aesDecryption, iv );


CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink( decryptedtext ) );
stfDecryptor.Put( reinterpret_cast<const unsigned char*>( (vector + 4) ), 16 );
stfDecryptor.MessageEnd();

但我也一样:

terminate called after throwing an instance of 'CryptoPP::InvalidCiphertext'
  what():  StreamTransformationFilter: invalid PKCS #7 block padding found

编辑2:

向量是用这一行创建的(向量的填充方式放在这里很复杂,因为我使用的是网络编码平台):

uint8_t* vector;

编辑3:

这就是我加密向量的方式。

CryptoPP::AES::Encryption aesEncryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH);
        CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption( aesEncryption, iv );

        CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink( ciphertext ) );
        stfEncryptor.Put( reinterpret_cast<const unsigned char*>( (vector + 4) ), 16 );
        stfEncryptor.MessageEnd();

然后我再次将密文放入向量中:

std::cout << std::endl << std::endl;
for(int i=0;i < 16; i++){
    *(vector+ i + 4) = (ciphertext[i]) ; 
}

【问题讨论】:

  • 准确发布 vector 是什么。它无法由您发布的代码确定。如果不是 std::vector,则将变量命名为与标准 C++ 类相同的名称不是一个好主意。
  • @Niemand - 您在第三次和第四次编辑中提供的信息有冲突。第三次编辑使用StreamTransformationFilter::DEFAULT_PADDING,即StreamTransformationFilter::PKCS_PADDING。第四次编辑使用StreamTransformationFilter::NO_PADDING。您确实应该将其简化为您正在使用的实际代码的最小工作示例。
  • @Niemand - 请从问题中删除第 4 次编辑,并将其添加为答案。然后,接受你自己的答案。这就是 Stack Overflow 的工作原理。

标签: c++ crypto++


【解决方案1】:
terminate called after throwing an instance of CryptoPP::InvalidCiphertext'
    what():  StreamTransformationFilter: invalid PKCS #7 block padding found

这很容易解决。捕获 Crypto++ InvalidCiphertext 异常。请参阅InvalidCiphertext Class Reference

-----

If I just send the vector without size:

CryptoPP::StringSource ss( vector+4, true, ...

我认为这可能与以下 StringSource 构造函数匹配,这意味着长度为 true,这可能意味着 1. 雪上加霜的是,附加的 StreamTransformationFilter 被强制转换为 bool pumpAll

StringSource (const byte *string, size_t length, bool pumpAll, BufferedTransformation *attachment=NULL)

您可能应该启用警告来捕捉这些事情。 -Wall -Wextra 是不错的选择。 (不过,没有什么可以帮助强制强制指向 bool 的指针。例如,请参阅 GCC 邮件列表上的 No pointer conversion warning for "bool" in C/C++)。

-----

If I send the vector and the size (it's really 20 but I
just want to decrypt the last 16 bytes):

...
CryptoPP::StringSource ss( vector+4, 16 , true, ...

我认为这不会按预期工作。 CBC 模式不是随机访问模式(但我可能解析不正确):

CBC_Mode< AES >::Encryption enc;
cout << "Random access: " << enc.IsRandomAccess() << endl;

结果:

Random access: 0

我相当肯定它不会以字节大小工作。您可以通过在解密器上方添加一层来做到这一点,但您必须管理初始化向量,然后解密一个块,最后将 offest 返回到块中。

-----

您可能应该执行以下操作。它效率不高,因为它将string 处理为vector,但我想尝试使用向量,因为您似乎正在使用它们。

encrypted.CopyTo(f2) 是您放置套接字代码的位置。您的代码不需要encrypted.CopyTo(f2)

// Creates the memory block and zero's it.
SecByteBlock key(AES::DEFAULT_KEYLENGTH), iv(AES::BLOCKSIZE);

/////////////////////////////////////////////////////////////

string m1;
vector<char> v1;

m1 = "Now is the time for all good men to come to the aide of their country";
copy(m1.begin(), m1.end(), back_inserter(v1));

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

ByteQueue encrypted;
StreamTransformationFilter f1(enc, new Redirector(encrypted));

f1.PutWord32((uint32_t)v1.size(), BIG_ENDIAN_ORDER);
f1.Put((const unsigned char *) &v1[0], v1.size());
f1.MessageEnd();

/////////////////////////////////////////////////////////////

string m2;
vector<char> v2;

CBC_Mode< AES >::Decryption dec;
dec.SetKeyWithIV(key, key.size(), iv, iv.size());

ByteQueue decrypted;
StreamTransformationFilter f2(dec, new Redirector(decrypted));

encrypted.CopyTo(f2);
f2.MessageEnd();

uint32_t len;
decrypted.GetWord32(len, BIG_ENDIAN_ORDER);

v2.resize(len);
decrypted.Get((unsigned char *) &v2[0], v2.size());

copy(v2.begin(), v2.end(), back_inserter(m2));

/////////////////////////////////////////////////////////////

cout << "Message: " << m1 << endl;
cout << "Decrypted: " << m2 << endl;

它产生了预期的结果:

$ ./cryptopp-test.exe
Message: Now is the time for all good men to come to the aide of their country
Decrypted: Now is the time for all good men to come to the aide of their country

-----

关于你对vector + 4的使用:一旦密文被解密,你就可以在里面寻找,并做一些事情,比如从中读取一个长度。这就是它的作用:

// Ciphertext is already decrypted
uint32_t len;
decrypted.GetWord32(len, BIG_ENDIAN_ORDER);

如果你只是想丢弃该值,那么试试:

decrypted.Skip(4);

但是你必须弄清楚如何调整vector 的大小。

-----

我正在研究一种用于安全通信的新协议

安全通信通常同时提供机密性和真实性保证。您似乎缺少后者,因为 CBC 模式仅提供机密性。

使用已经存在的东西可能是个好主意,例如 IPSec(首选)或 TLS(将在紧要关头使用)。此外,关键协议将成为一个痛点。这是考虑使用 IPSec 或 TLS 之类的另一个原因。

如果您不能使用已有的东西,那么您应该使用Authenticated Encryption 模式,以便将机密性和真实性结合到一个模式中。尝试EAXGCMCCM 模式。

如果你这样做了,那么改变是相当微不足道的:

    EAX< AES >::Encryption enc;
    enc.SetKeyWithIV(key, key.size(), iv, iv.size());

    ByteQueue encrypted;
    AuthenticatedEncryptionFilter f1(enc, new Redirector(encrypted));

还有:

    EAX< AES >::Decryption dec;
    dec.SetKeyWithIV(key, key.size(), iv, iv.size());

    ByteQueue decrypted;
    AuthenticatedDecryptionFilter f2(dec, new Redirector(decrypted));

-----

您可以通过将密文复制到编码器来打印密文。复制是非破坏性的(与使用TransferTo 移动它不同):

string encoded;
HexEncoder encoder(new StringSink(encoded));
encrypted.CopyTo(encoder);
encoder.MessageEnd();

Fox CBC 模式(不太安全):

Encrypted: 7F9FFCAB00704EC79BB5F19C48FE7C668033B16F52E7E00671A38A06F4A7426E7FE31
95CA6A83C7414A76C250B42E63143C93E7A6B97B6304C5782DE3E62BD545706A9F62CD7AD57BC374
19B7510EBED

对于 EAX 模式(更安全):

Encrypted: B75347EB75DF8E1F0424979E91CEECD455F5727B506A8AA932AF07E1DF6A7B037A245
FEC7A2270BFAB8110226769E1C0A12E95C455E9C714AF28DA330A2B01B3F2D541D4E68276193C018
7BA0246166AD26624E848EC8330D3

EAX 模式下的额外字节是身份验证标签。它用于检测意外和恶意篡改。

【讨论】:

  • 感谢您的广泛回答,但我还是有点迷失在这里。 “向量”是一个很大的内存空间,我有我所有的编码数据包。所以我解密最后 16 个字节并切换到下一个数据包,依此类推。 - 这个解密只是协议的一小部分,其余的都在使用 uint8_t,这就是为什么我不能改变它。我很困惑,因为我只想获取 16 个字节,然后解密它们并将它们再次放入向量中。而且它变得越来越复杂。我添加了我的加密方式,你可以看到 vector + 4 工作,也许我可以做类似的事情?
  • " "vector" 是一个很大的内存空间,我有我所有的编码数据包" - 在 C++ 中,vector 是一个数据结构。见std::vector
【解决方案2】:

问题是我使用填充进行编码并试图在没有填充的情况下对其进行解密。所以我不仅在加密中添加,而且在解密中添加它应该在没有填充的情况下工作。

创建密钥和 IV:

CBC_Mode< AES >::Encryption encryptor;
encryptor.SetKeyWithIV( key, CryptoPP::AES::DEFAULT_KEYLENGTH, iv );

加密:

CryptoPP::StringSource ss( vector + 4 , 16, true, 
     new CryptoPP::StreamTransformationFilter( encryptor,
          new CryptoPP::StringSink( ciphertext ),
              CryptoPP::StreamTransformationFilter::NO_PADDING
     ) // StreamTransformationFilter      
); // StringSource

解密:

CBC_Mode< AES >::Decryption decryptor;
    decryptor.SetKeyWithIV( key, CryptoPP::AES::DEFAULT_KEYLENGTH, iv );

        CryptoPP::StringSource ss( reinterpret_cast<const unsigned char*>( (vector + 4) ), 16, true,
                new CryptoPP::StreamTransformationFilter( decryptor,
                    new CryptoPP::StringSink( decryptedtext ), CryptoPP::StreamTransformationFilter::NO_PADDING) );

我这样做是因为我不想再使用填充。

【讨论】:

  • “我这样做是因为我不想再使用填充了” - 请注意这一点。这意味着您将始终必须以块大小(16 字节)的块的形式处理数据。你不能让 AES 在小于这个大小的情况下工作。
  • 另外,我不知道vector[0 - 3] 的内容。即使它没有加密,它也应该被 MAC'd 作为 AAD。同样,请参阅 Crypto++ wiki 上的 Authenticated Encryption
  • vector[0-3] 有一个编码向量,我也使用Network Coding 的一些变体。
  • 我不会称之为答案,它并没有解决为什么首先抛出异常的问题。
猜你喜欢
  • 2019-09-02
  • 1970-01-01
  • 1970-01-01
  • 2013-08-11
  • 1970-01-01
  • 2017-12-10
  • 2019-06-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多