katit
katit

Reputation: 17915

Decrypt data using private key fails when code running under IIS

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

Answers (2)

bgman
bgman

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

katit
katit

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

Related Questions