I am currently trying to call an API that requires me to put XML data in a PKCS#7 format.
This data is posted to the API end point.
The response also comes in a form of a PKCS#7 cryptographic messages (MIME-type is application/pkcs7-mime).
Some Notes they provide: A cryptographic message doesn't contain any certification chains. Data compression is not used. Data encryption is not used. A cryptographic message is in the OpenSSL PEM format.
I have been supplied two certificates. One I created the request for and have the private key, the other supplied to me by the service provider.
I have managed to successfully install these certificates, and can communicate with the service.
I am seemingly sending data to this API service successfully.
Now I'm trying to make sense of the response I'm receiving from this API.
This response looks like this
-----BEGIN PKCS7-----
WISGCSqGSIb3DQEHSqCSWISCSQExCzSJBgUrDgWCGgUSWISGCSqGSIb3DQEHSSCS
AfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsd
WISGCSqGSIb3DQEHSqCSWISCSQExCzSJBgUrDgWCGgUSWISGCSqGSIb3DQEHSSCS
AfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsd
WISGCSqGSIb3DQEHSqCSWISCSQExCzSJBgUrDgWCGgUSWISGCSqGSIb3DQEHSSCS
AfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsd
WISGCSqGSIb3DQEHSqCSWISCSQExCzSJBgUrDgWCGgUSWISGCSqGSIb3DQEHSSCS
AfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsd
WISGCSqGSIb3DQEHSqCSWISCSQExCzSJBgUrDgWCGgUSWISGCSqGSIb3DQEHSSCS
AfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsd
WISGCSqGSIb3DQEHSqCSWISCSQExCzSJBgUrDgWCGgUSWISGCSqGSIb3DQEHSSCS
AfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsd
WISGCSqGSIb3DQEHSqCSWISCSQExCzSJBgUrDgWCGgUSWISGCSqGSIb3DQEHSSCS
AfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsd
WISGCSqGSIb3DQEHSqCSWISCSQExCzSJBgUrDgWCGgUSWISGCSqGSIb3DQEHSSCS
AfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsdfdAfsdFAD3433423ASfdsd
-----END PKCS7-----
(I jumbled up the contents on the off chance there's anything sensitive in there)
With this response i need to
- Verify the digital signature to ensure that response was sent by the provider
- Get an Xml formatted message from this response
I have been primarily using the Bouncy Castle library and the MS SignedCms Class
In summary, I am getting absolutely nowhere.
Please can someone guide me on what to do here as I've been at this for about 5 days and am going nowhere fast.
Here is some of what I'm doing so far:
Make the request
Using the HttpWebRequest and HttpWebResponse i am posting data to the service using my supplied certificate
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;
Here I get back the "BEGIN PKCS7" message from above.
Now I'm trying to figure out what to do with this
MS Signed CMS Class Approach
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)
}
This throws the exception on "signedCms.Decode" of "ASN1 bad tag value met."
BouncyCastle ISigner
Here the documentation is non-existent.
So first I save my response to a file and use the TextReader object to try test with 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());
}
This returns the result: "1.2.840.113549.1.7.2"
From what i can tell, this means its "Pkcs7 Signed Data"
Woohoo, something looks like its working.
But from here, how do I verify, and how do I extract any information from this
My Verify attempt
using (TextReader reader = File.OpenText(@"c:\temp\resultString.txt"))
{
PemReader pemRd = new PemReader(reader);
var signature = new CmsSignedData(pemRd.ReadObject());
}
Fail - signature is null
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());
Fail - need private key to create signer
In Closing
I hope I have provided enough information to get some help here.
Maybe I'm heading in completely the wrong direction.
My Questions are:
- How do I Verify the digital signature?
- How do I get an Xml formatted message from this response?
THE SOLUTION
Thanks to gtrig, i finally have a solution.
Using the MS SignedCms object I had to first remove the Header and Footer form the message, then Convert.FromBase64String
Working Solution
SignedCms signedCms = new SignedCms();
resultString = resultString.Replace("\n", "").Replace("-----BEGIN PKCS7-----", "").Replace("-----END PKCS7-----", "");
signedCms.Decode(Convert.FromBase64String(resultString));
Now signedCms.ContentInfo.Content contains the reponse Xml message I expect