C# 从C调用时CFB模式的截断输出#

C# 从C调用时CFB模式的截断输出#,c#,c++,encryption,pinvoke,crypto++,C#,C++,Encryption,Pinvoke,Crypto++,我有一个相当烦人的问题,我两天都无法解决。我有一个使用C++编写的密码+ +库的加密()/>代码方法。该方法实现如下: string CRijndaelHelper::Encrypt(string text) { CFB_Mode< AES >::Encryption e; e.SetKeyWithIV(_key, sizeof(_key), _iv); string cipher, encoded; // CFB mode must not us

我有一个相当烦人的问题,我两天都无法解决。我有一个使用C++编写的密码+ +库的<代码>加密()/>代码方法。该方法实现如下:

string CRijndaelHelper::Encrypt(string text)
{
    CFB_Mode< AES >::Encryption e;
    e.SetKeyWithIV(_key, sizeof(_key), _iv);

    string cipher, encoded;

    // CFB mode must not use padding. Specifying
    //  a scheme will result in an exception
    StringSource ss(text, true,
        new StreamTransformationFilter(e,
            new StringSink(cipher)
        ));     

    return cipher;
};
[DllImport("EncryptDecrypt.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string encrypt([MarshalAs(UnmanagedType.LPStr)] string contents);
而PInvoke部分如下所示:

string CRijndaelHelper::Encrypt(string text)
{
    CFB_Mode< AES >::Encryption e;
    e.SetKeyWithIV(_key, sizeof(_key), _iv);

    string cipher, encoded;

    // CFB mode must not use padding. Specifying
    //  a scheme will result in an exception
    StringSource ss(text, true,
        new StreamTransformationFilter(e,
            new StringSink(cipher)
        ));     

    return cipher;
};
[DllImport("EncryptDecrypt.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string encrypt([MarshalAs(UnmanagedType.LPStr)] string contents);
一切看起来都很正常,除了我在
transformedText
变量中只加密了前540个字节,仅此而已

请告知

。。。返回从本机代码调用时正确加密的字符串

问题不是你的C++ >代码>加密<代码>,它使用了<代码> STD::String < /C>。问题在于将其作为

char*
封送回托管代码

CRijndaelHelper::Encrypt
更改为以下内容,以删除嵌入的
NULL
,该值将以1/255的概率自由分布:

#include <cryptopp/base64.h>
using CryptoPP::Base64Encoder;
...

string CRijndaelHelper::Encrypt(string text)
{
    CFB_Mode< AES >::Encryption e;
    e.SetKeyWithIV(_key, sizeof(_key), _iv);

    string cipher, encoded;

    // CFB mode must not use padding. Specifying
    //  a scheme will result in an exception
    StringSource ss(text, true,
        new StreamTransformationFilter(e,
            new Base64Encoder(
                new StringSink(cipher)
        )));     

    return cipher;
};

你的根本问题是你使用了错误的类型。加密使用字节数组,而不使用文本。您需要停止使用
string
char*
等等。您需要在非托管代码中使用
unsigned char*
,在托管代码中使用
byte[]

你可能很难接受这个建议,但我还是照样给你。除了将零字节视为空终止符(截断的原因)的问题之外,您当前的方法完全忽略了文本编码的问题。你不能那样做

您可能有很好的理由希望将一个字符串转换为另一个字符串。这很好,但是转换的加密部分需要在字节数组上操作。处理文本到文本加密的方法是使用一系列转换。加密时的转换如下所示:

  • 使用定义良好的编码将字符串转换为字节数组。例如,UTF-8
  • 加密字节数组,这将导致输出另一个字节数组
  • 使用例如base64将此加密字节数组编码为文本
  • 这尊重了加密操作在字节数组上的事实,以及对文本编码的明确性

    在相反方向上,您可以反转步骤:

  • 将base64解码为加密的字节数组
  • 解密此字节数组
  • 使用UTF-8解码解密的字节数组以获得原始字符串

  • 只是猜测一下,但是
    rij.Encrypt(cont)
    可能会返回一个在位置540附近有0x00字节的数组。百科文本应该均匀分布在[0-255]上,因此在26KB中应该有大量的then。修复方法是将返回值从
    char*
    更改为包含长度的值。使用
    API\u EXPORT int\u cdecl encrypt(在unsigned char*IN、OUT unsigned char*OUT、INOUT unsigned int*osize中)重新设计该接口可能更好。
    成功接收xml字符串并返回从本机代码调用时正确加密的字符串。CryptoPP支持StringSink目标,从代码中可以看出。整个问题是影响API的PInvoke调用会发生什么。我可以肯定地告诉您,
    StreamTransformationFilter(e,new StringSink(cipher))
    最终将在其中嵌入一个
    NULL
    。因为C++字符串具有C字符数组和长度,所以<代码>字符串< /代码>将处理它。一个
    char*
    不会。你就是那个男人!:)现在工作。如果没有帮助,我想我永远也不会明白这一点。我唯一难以理解的是,本机构造的std::string与使用从CLR封送的char*数组初始化的std::string之间的实际区别是什么。编组对这个字符*的实际作用是什么,导致加密失败?事实上,它发生在封送回托管之前。同意你的看法,David,但不幸的是,公司正在使用许多文件,这些文件已经用这种方法加密,因此我担心更改编码可能会导致问题。CRijndaelHelper::Encrypt()代码在我开始实现之前就已经编写好了。然而,上面JWW的方法,加上你的回答,对我帮助很大。谢谢你接受的答案表明方法有所改变,与我的建议没有太大的不同。另一个答案没有解决的是遇到非ASCII文本的可能性。这正是我想说的。@aquila-David在设计问题上是对的。我只帮助解决了一个非常小的问题(嵌入的
    NULL
    导致截断)。你还有更大的问题需要解决。虽然David建议使用UTF-8,但我想说的是,为了便于携带,您必须使用它。@jww感谢您的慷慨评论。我想说UTF8不是唯一可能的选择。虽然这很可能是最好的。有时UTF16可能是最好的。然而,关键是用于从文本到字节数组(以及返回)的编码是以明确的方式定义的。