c# - 由 PKCS#7 加密消息保护的 API

标签 c# x509certificate pkcs#7

我目前正在尝试调用一个 API,该 API 要求我将 XML 数据放入 PKCS#7 格式。
此数据发布到 API 端点。
响应也以 PKCS#7 加密消息的形式出现(MIME 类型是 application/pkcs7-mime)。
他们提供的一些注释:加密消息不包含任何认证链。不使用数据压缩。不使用数据加密。加密消息采用 OpenSSL PEM 格式。

我已经获得了两份证书。一个是我创建请求并拥有私钥的,另一个是服务提供商提供给我的。
我已经成功安装了这些证书,并且可以与服务通信。

我似乎成功地向这个 API 服务发送了数据。
现在,我正在尝试理解我从该 API 收到的响应。
这个响应看起来像这样

-----BEGIN PKCS7-----
WISGCSqGSIb3DQEHSqCSWISCSQExCzSJBgUrDgWCGgUSWISGCSqGSIb3DQEHSSCS
AfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsd
WISGCSqGSIb3DQEHSqCSWISCSQExCzSJBgUrDgWCGgUSWISGCSqGSIb3DQEHSSCS
AfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsd
WISGCSqGSIb3DQEHSqCSWISCSQExCzSJBgUrDgWCGgUSWISGCSqGSIb3DQEHSSCS
AfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsd
WISGCSqGSIb3DQEHSqCSWISCSQExCzSJBgUrDgWCGgUSWISGCSqGSIb3DQEHSSCS
AfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsd
WISGCSqGSIb3DQEHSqCSWISCSQExCzSJBgUrDgWCGgUSWISGCSqGSIb3DQEHSSCS
AfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsd
WISGCSqGSIb3DQEHSqCSWISCSQExCzSJBgUrDgWCGgUSWISGCSqGSIb3DQEHSSCS
AfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsd
WISGCSqGSIb3DQEHSqCSWISCSQExCzSJBgUrDgWCGgUSWISGCSqGSIb3DQEHSSCS
AfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsd
WISGCSqGSIb3DQEHSqCSWISCSQExCzSJBgUrDgWCGgUSWISGCSqGSIb3DQEHSSCS
AfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsd
-----END PKCS7-----

(我把内容弄混了,怕里面有敏感内容)

有了这个回应我需要

  1. 验证数字签名以确保响应是由提供商发送的
  2. 从此响应中获取 Xml 格式的消息

我一直主要使用 Bouncy Castle图书馆和 MS SignedCms Class
总而言之,我一事无成。

请有人指导我在这里做什么,因为我已经在这里工作了大约 5 天,而且很快就无处可去。

这是我目前正在做的一些事情:

提出请求

使用 HttpWebRequest 和 HttpWebResponse 我使用我提供的证书将数据发布到服务

var store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2 cert = store.Certificates.Find(X509FindType.FindByThumbprint, "ACLKJCLKJCLKJCLKJCLKJCLKJCLKJCLKJCLKJCLK", false)[0];

HttpWebRequest request = null;
var uri = new Uri(endPointUri);
request = (HttpWebRequest) WebRequest.Create(uri);
request.Method = "POST";
request.ContentType = "application/pkcs7-mime";
request.ContentLength = requestString.Length;
request.ClientCertificates.Add(cert);

using (Stream writeStream = request.GetRequestStream())
{
    var encoding = new UTF8Encoding();
    byte[] bytes = encoding.GetBytes(requestString);
    writeStream.Write(bytes, 0, bytes.Length);
}
string result = null;
using (var response = (HttpWebResponse) request.GetResponse())
{
    using (Stream responseStream = response.GetResponseStream())
    {
        if (responseStream != null)
        {
            using (var readStream = new StreamReader(responseStream, Encoding.UTF8))
            {
                result = readStream.ReadToEnd();
            }
        }
    }
}

return result;

在这里,我从上面取回了“BEGIN PKCS7”消息。
现在我想弄清楚如何处理这个

MS 签名 CMS 类方法

SignedCms signedCms = new SignedCms();
signedCms.Decode(Encoding.Default.GetBytes(resultString));
try
{
    signedCms.CheckSignature(new X509Certificate2Collection(cert1), true);
}
catch (System.Security.Cryptography.CryptographicException e)
{
    _Log.Error(e.Message)
}

这会引发“已满足 ASN1 错误标记值”的“signedCms.Decode”异常。

BouncyCaSTLe ISigner

这里没有文档。
所以首先我将我的响应保存到一个文件并使用 TextReader 对象尝试使用 BouncyCaSTLe 进行测试

using (TextReader reader = File.OpenText(@"c:\temp\resultString.txt"))
{
    PemReader pemRd = new PemReader(reader);
    ContentInfo d = (ContentInfo)pemRd.ReadObject();
    Console.WriteLine(d.ContentType.ToString());
}

返回结果:“1.2.840.113549.1.7.2”
据我所知,这意味着它的“Pkcs7 签名数据”
哇哦,有些东西看起来像是在工作。
但是从这里,我如何验证,以及如何从中提取任何信息

我的验证尝试

using (TextReader reader = File.OpenText(@"c:\temp\resultString.txt"))
{
    PemReader pemRd = new PemReader(reader);
    var signature = new CmsSignedData(pemRd.ReadObject());
}

失败 - 签名为空

var store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2 cert = store.Certificates.Find(X509FindType.FindByThumbprint, "ACLKJCLKJCLKJCLKJCLKJCLKJCLKJCLKJCLKJCLK", false)[0]; //tried with both certs
ISigner signer = SignerUtilities.GetSigner("RSA");
var bouncyx509 = DotNetUtilities.FromX509Certificate(cert1);
signer.Init(true, DotNetUtilities.FromX509Certificate(cert1).GetPublicKey());

失败 - 需要私钥才能创建签名者

收盘中

我希望我已经提供了足够的信息来获得一些帮助。
也许我正朝着完全错误的方向前进。

我的问题是:

  1. 如何验证数字签名?
  2. 如何从该响应中获取 Xml 格式的消息?

解决方案

感谢gtrig,我终于有了解决方案。
使用 MS SignedCms 对象,我必须首先从消息中删除页眉和页脚,然后是 Convert.FromBase64String

工作解决方案

SignedCms signedCms = new SignedCms();
resultString = resultString.Replace("\n", "").Replace("-----BEGIN PKCS7-----", "").Replace("-----END PKCS7-----", "");
signedCms.Decode(Convert.FromBase64String(resultString));

现在 signedCms.ContentInfo.Content 包含我期望的响应 Xml 消息

最佳答案

这是一个不完整的答案,但它可能会让你更进一步。

如果你有权访问 openssl,试试这个 command查看它是否可以读取数据:

openssl pkcs7 -in resultString.txt -text

在那之后在你的代码中尝试这个(与你所拥有的唯一区别是“Content”而不是“ContentType”:

Console.WriteLine(d.Content.ToString());

signedCms.Decode() 采用字节数组,这可能是 DER 格式的消息,而不是您在响应中收到的 PEM 格式的消息。要在字节数组中获取它,您必须去除页眉和页脚(开始/结束)行并将其余部分传递给此方法:

Convert.FromBase64String()

或者,您可以使用 openssl 将文件转换为 DER 格式,然后直接从文件中读取字节。

openssl pkcs7 -in resultString.txt -outform DER -out result.der

关于c# - 由 PKCS#7 加密消息保护的 API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18225897/

相关文章:

c# - 你如何在 CaSTLe MVC (Monorail) 中获取用户的 IP 地址?

java - 如何使用x509证书生成数字签名?

spring - 如何让证书进入X509过滤器(Spring Security)?

c# - 调用 SignedCMS.Decode 花费的时间太长

c# - 使用预先计算的签名在 C# 中创建 PKCS#7 签名消息

c# - 什么时候不要使用 'this' 关键字?

c# - 多层命名

node.js - 如何创建签名文件以从nodejs服务器在safari中发送网络推送通知

c# - 在 C# 中运行 F# 代码 -(就像在 C# 中使用 Roslyn 的 C#)

java - 使用 DEROctetString 与纯扩展