Reputation: 17915
I have a code which runs under IIS as a web service. So, on message receipt, I load certificate from database, check it(to make sure cert is valid and with a private key) and call following function. Obviously all of that works using test cases, but doesn't work under IIS in production. I was able to find some information online which point to the fact that IIS runs under NETWORK credentials and doesn't have permissions needed.
But error message is misleading and there is no files involved in this process.
Getting cert and checking it's good:
this.ServerCertificate = new X509Certificate2(options.As2ServerCertificate);
if (!this.ServerCertificate.HasPrivateKey || this.ServerCertificate.NotAfter < DateTime.Now.Date)
{
return false;
}
When calling this function:
public static byte[] Decrypt(byte[] encodedEncryptedMessage, X509Certificate2 certificate)
{
var envelopedCms = new EnvelopedCms();
envelopedCms.Decode(encodedEncryptedMessage);
envelopedCms.Decrypt(new X509Certificate2Collection(certificate));
return envelopedCms.Encode();
}
In production we receive following error:
The system cannot find the file specified.
System.Security at System.Security.Cryptography.Pkcs.EnvelopedCms.DecryptContent(RecipientInfoCollection recipientInfos, X509Certificate2Collection extraStore) at Edi.CommunicationProtocols.AS2.Cryptography.Decrypt(Byte[] encodedEncryptedMessage, X509Certificate2 certificate) in ClientServerCode\trunk\Edi\CommunicationProtocols\AS2\Cryptography.cs:line 44 at Edi.CommunicationProtocols.AS2.As2Server.ReceiveMessage(List`1 headers, Byte[] content) in Edi\CommunicationProtocols\AS2\As2Server.cs:line 41 at Web.Services.Rest.AS2ListenerService.ProcessMessage(String accountId, Stream data) in ClientServerCode\trunk\Web.Services\Rest\AS2ListenerService.cs:line 91
So, it looks like framework trying to read something off the disk even though all data (certificate) provided by code. How do I work around this?
Upvotes: 0
Views: 251
Reputation: 334
Faced same issue and switched to bouncy castle:
public static byte[] Decrypt(byte[] data, byte[] certbytes, string password)
{
Pkcs12Store pkcs12 = new Pkcs12Store(new MemoryStream(certbytes), password.ToArray());
string keyAlias = pkcs12.Aliases.Cast<string>().First(x => pkcs12.IsKeyEntry(x));
AsymmetricKeyParameter key = pkcs12.GetKey(keyAlias).Key;
Org.BouncyCastle.X509.X509Certificate cert = pkcs12.GetCertificate(keyAlias).Certificate;
var envelopedData = new CmsEnvelopedData(data);
var recepientInfos = envelopedData.GetRecipientInfos();
var recepientId = new RecipientID()
{
Issuer = cert.IssuerDN,
SerialNumber = cert.SerialNumber
};
var recepient = recepientInfos[recepientId];
return recepient.GetContent(key);
}
As you are doing As2 and dealing with multipart content, just use Mimekit and you'll end up with something like:
public static MimeEntity Decrypt(ApplicationPkcs7Mime data, byte[] certbytes, string password)
{
using (var ctx = (SecureMimeContext)CryptographyContext.Create("application/pkcs7-mime"))
{
ctx.Import(new MemoryStream(certbytes), password);
return data.Decrypt(ctx);
}
}
As a side note, on Azure you don't have this issue
Upvotes: 1
Reputation: 17915
So, what happens here, is that even though I do pass certificate to my method call, framework still tries to lookup other certificates from user profile and fails.
Solution to this problem (also not ideal) is to do following: CryptographicException was unhandled: System cannot find the specified file
After setting "Load user profile: true" all is working
Upvotes: 0