【问题标题】:How to decrypt a PKCS7 message如何解密 PKCS7 消息
【发布时间】:2021-09-30 18:20:31
【问题描述】:

我无法在 C# 中解密一些 XML 文件。

我对密码学知之甚少,但据我了解,我有一个受密码保护的证书,其中包含私钥和公钥。

通过将密码应用于证书,我获得了解密收到的文件所需的私钥。

我要解密的消息位于 PKCS7 (CMS?) 格式的文件中。我相信我需要解析文件,解密它们,然后取消签名。

我不知道如何做到这一点。我已经尝试了我发现的工具,这些工具显然可以帮助我完成这项任务,但我无法让它发挥作用。

此时,我真的很想知道;有什么我不明白的吗?还是我在工作中使用了错误的工具?

这是我在一堆代码中的一些尝试:

public string DecryptFailingExample()
{
    var content = File.ReadAllBytes("encryptedFileWithDataThatIWant.xml.enc");
    var cert = new X509Certificate2("certificateFile.pfx", "PasswordForFile");

    //This code didn't work. rsa.Decrypt would throw: "The length of the data to decrypt is not valid for the size of this key."
    //using RSA rsa = cert.GetRSAPrivateKey();
    //var decryptedBytes = rsa.Decrypt(content, RSAEncryptionPadding.OaepSHA1); //I tried all possible paddings - Nothing worked


    //So I tried this instead:
    SignedCms signedCms = new SignedCms();
    signedCms.Decode(content); //Crash: ASN1 corrupted data; The provided data is tagged with 'Universal' class value '13', but it should have been 'Universal' class value '16'.
    var ecms = new EnvelopedCms();
    
    //If I try to skip SignedCms and pass "content" directly to EnvelopedCms, I will get: "ASN1 bad tag value met."
    ecms.Decode(signedCms.ContentInfo.Content); //Crash: 
    ecms.Decrypt(new X509Certificate2Collection(cert));
}

这是我想要解密的消息的样子:

-----开始PKCS7-----

MIAGCSqGSIb3DQEHA6BAMIACAQAxggGEMIIBgAIBADBoMFExCzAJBgNVBAYTAkRF MSQwIgYDVQQKExtFdXJvcGVhbiBFbmVyZ3kgRXhjaGFuZ2UgQUcxHDAaBgNVB4MT

3nPyd3c9iGyKhWdQPPr/SRWB/l9bCjkla81MgTcj1rRGQyPJXpkyxpc9U4YYZnxt eHkcJMVWDw9ErThok8nh/7bkE4KbWBaLZAac+9occ4deDrlu0wAAAAAAAAAAAAA=

-----结束PKCS7-----

编辑: 这是另一个不起作用的最小示例。

//Step 1: Load PKCS7 message from disk
var content = File.ReadAllBytes("encryptedFileWithDataThatIWant.xml.enc");

//Step 2: Load .pfx certificate from disk 
var cert = new X509Certificate2("certificateFile.pfx", "Password");

//Step 3: Create instance of EnvelopedCms
var ecms = new EnvelopedCms();

//Step 4: Add certificate for decoding
ecms.Certificates.Add(cert);

//Step 5: Decode the PKCS7 message
ecms.Decode(content); //Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'ASN1 bad tag value met.'

除了 .PFX 文件,我还有一个带有私有证书的 PEM 文件。它看起来像这样:

-----开始 RSA 私钥--

Proc-Type:4,加密 DEK-信息:AES-256-CBC,915538553FEFBA6A98930E4BFFDA1E68

Pk0FHxXNHukA62FSmuzzE+gqHgeauZr6z+lqTcbf55cakrQzHQOQUnR0w5kCFPPg

L7U2cWJpbNsRNmBcTyH1WWJ4hYoCqdl9G6Zey4y/EQbZl1DKXtmIiLZSneF0VU9u

-----结束 RSA 私钥-----

我尝试像这样导入密钥,但这似乎不起作用:

var pem = File.ReadAllBytes("pemFile.pem");
var pemChars = Encoding.UTF8.GetString(pem);
var rsa = RSA.Create();
rsa.ImportFromEncryptedPem(pemChars, "Password"); //System.ArgumentException: 'No supported key formats were found. Check that the input represents the contents of a PEM-encoded key file, not the path to such a file. (Parameter 'input')'

【问题讨论】:

  • 是的,PKCS#7 指定 CMS,查找 RFC。您应该首先进行 PEM 解码,然后使用结果调用 EnvolopedCms#Decode(binaryDerEncoding)。通常消息是经过签名然后加密的(当然,如果它们被签名的话)。
  • @MaartenBodewes 谢谢你的提示。在 EnvelopedCms.Decode(binaryDerEncoding) 之前,您希望“PEM 解码”部分是什么样子?我承认我很难将我认为应该发生的事情翻译成代码。在我的脑海中,我应该能够:加载证书(.PFX),应用正确的密码->从证书中提取私钥->使用私钥解密消息。但根据你的回答,我想我错过了一步?对不起,如果我听起来很困惑。我是。
  • PKCS#7 是二进制编码。 PEM 使用带有页眉和页脚行的 base 64 将其转换为文本(最初是为了使其可以在 S/MIME/电子邮件中使用)。您当前拥有的代码似乎需要二进制编码。如果你给Decode 函数提供文本,它会在尝试对文本进行二进制解码时崩溃。因此,您需要一个已添加到 .NET 最新版本中的 PEM 解析器(我知道,因为我前段时间已经为它们编写了一些代码,开源)。当然任何 PEM 解析器都可以,Bouncy Castle 也包含一个。

标签: c# encryption rsa x509certificate pkcs#7


【解决方案1】:

知道了!

感谢@MaartenBodewes 让我走上正轨。 我要解码的文件不是二进制文件,而是文本文件。将文件作为文本读取,然后转换为二进制文件就足够了。

工作代码大致如下所示,对于我的具体情况,可以进一步减少。事实上,它将处理 PKCS7 和 CMS 标签 + 二进制和文本文件。

public string Decrypt()
{
    var ecms = Read("encryptedFileWithDataThatIWant.xml.enc");
    var cert = new X509Certificate2("certificateFile.pfx", "Password");
    ecms.Decrypt(new X509Certificate2Collection{cert});
    return Encoding.UTF8.GetString(ecms.ContentInfo.Content);
}

private static EnvelopedCms Read(string fileName)
{
    //Try read as binary
    var maybeFirstException = TryRead(File.ReadAllBytes(fileName), out var ecms);
    if (maybeFirstException is null)
    {
        return ecms;
    }

    //Otherwise read as text, convert to binary and try again
    var text = File
        .ReadAllText(fileName)
        .Replace("-----BEGIN PKCS7-----", string.Empty)
        .Replace("-----BEGIN CMS-----", string.Empty)
        .Replace("-----END PKCS7-----", string.Empty)
        .Replace("-----END CMS-----", string.Empty);

    
    var binary = Convert.FromBase64String(text);
        
    var maybeSecondException = TryRead(binary, out ecms);
    if (maybeSecondException is null)
    {
        return ecms;
    }

    throw new AggregateException("Tried to decode file as both binary and text, but both attempts failed", new List<Exception>
    {
        maybeFirstException,
        maybeSecondException
    });
}

private static Exception TryRead(byte[] bytes, out EnvelopedCms ecms)
{
    ecms = new EnvelopedCms();
    try
    {
        ecms.Decode(bytes);
        return null;
    }
    catch (CryptographicException e)
    {
        return e;
    }
}

【讨论】:

  • 我遵循了相同的方法,并且 ecms.ContentInfo.Content 返回我的有效负载以及未知字符。我哪里错了?
  • 我仍然不是该主题的专家,但除了我在这里得到的 gelp 之外,我发现对我的情况有帮助的是更深入地查看我手头的 PKCS7 消息。你的文件是二进制的吗?还是纯文本?它有 PKCS7 标题/拖车吗?既然你效仿了我的例子,那你的结果肯定有什么不同。当然也有可能 PKCS7 消息确实包含胡言乱语。你能排除这种可能性吗?
  • 我的文件有 PKCS7 预告片。我尝试使用 ChilkatDnCore 解密相同的消息。不幸的是,它只付了一个。
猜你喜欢
  • 2022-01-10
  • 1970-01-01
  • 2011-08-04
  • 2023-01-04
  • 2021-04-21
  • 2012-12-11
  • 1970-01-01
  • 2018-06-01
  • 1970-01-01
相关资源
最近更新 更多