Key is only allowed to be used for signing, not for decryption

0 votes
asked Oct 12 by michalis.viglakis (200 points)

Regarding this enum value
Assume I have a certificate with only signing purposes.

Certificate.Validate returns valid certificate.(passing IgnoreWrongUsage does not change anything)

When I pass this certificate to envelopedData.Decrypt();

Key is only allowed to be used for signing, not for decryption.

How can I get this before calling decrypt method?

Thanks

2 Answers

0 votes
answered Oct 12 by Lukas Matyska (47,230 points)

Can you please post here content of GetIntendedUsage() and GetEnhancedUsage() for your certificate.

It can be displayed by this simple routine:

var cert = Certificate.LoadPfx("path.pfx", "password");
Console.WriteLine(cert.GetIntendedUsage());
if (cert.GetEnhancedUsage() != null)
{
    Console.WriteLine(string.Join(", ", cert.GetEnhancedUsage()));
}
commented Oct 13 by michalis.viglakis (200 points)
edited Oct 15 by michalis.viglakis
Here is the result of the above code:
EncipherOnly, CrlSign, KeyCertSign, KeyAgreement, DataEncipherment, KeyEncipherment, NonRepudiation, DigitalSignature, DecipherOnly

Fyi, cert.GetEnhancedUsage() is null

I created this certificate from visual studio :
click project properties-> signing tab-> sign the clickonce mainfests -> create test certificate. Although this certificate is created for signing, it has all the attributes as i mention above executing your code snippet.
commented Oct 13 by michalis.viglakis (200 points)
edited Oct 15 by michalis.viglakis
In addition, i would like to decrypt even if the certificate is only for signing
0 votes
answered Oct 15 by Lukas Matyska (47,230 points)

Thank you for the details.

The Visual Studio created the certificate in such way that the private key was stored in Microsoft key storage with flag limiting the usage of the key for signing operations only. The certificate's private key can not be used for decryption in this case.

You can try it for yourself using system classes (no Rebex code is involved) like this:

var x509 = new X509Certificate2("path.pfx", "password");
var rsa = x509.PrivateKey as RSACryptoServiceProvider;

// Sign and verify is OK
Console.WriteLine("Signing...");
var sig = rsa.SignData(new byte[20], "SHA1");
Console.WriteLine("Validating...");
var valid = rsa.VerifyData(new byte[20], "SHA1", sig);
Console.WriteLine("Valid: {0}", valid);

// Decrypt fails
Console.WriteLine("Encrypting...");
var enc = rsa.Encrypt(new byte[20], false);
Console.WriteLine("Decryptig...");
var msg = rsa.Decrypt(enc, false);
Console.WriteLine("Success: {0}", BitConverter.ToString(msg) == BitConverter.ToString(new byte[20]));

However, you can workaround this with Rebex classes by exporting and importing the private key. It can be done like this:

// load original certificate
var cert = Certificate.LoadPfx("path.pfx", "password");

// export private key to new CSP
var rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(cert.GetRSAParameters(true, false));

// associate new CSP with original certificate
cert.Associate(rsa, false);

// Signing is OK
Console.WriteLine("Signing...");
var sig = cert.SignMessage(new byte[20]);
Console.WriteLine("Validating...");
var valid = cert.VerifyMessage(new byte[20], sig);
Console.WriteLine("Valid: {0}", valid);

// Decryption is OK
Console.WriteLine("Encrypting...");
var enc = cert.Encrypt(new byte[20]);
Console.WriteLine("Decryptig...");
var msg = cert.Decrypt(enc, false);
Console.WriteLine("Success: {0}", BitConverter.ToString(msg) == BitConverter.ToString(new byte[20]));
...