Reputation: 3081
Here is my code.
X509Certificate pXCert = new X509Certificate2(@"keyStore.p12", "password");
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)pXCert.PrivateKey;
string id = CryptoConfig.MapNameToOID("SHA256");
return csp.SignData(File.ReadAllBytes(filePath), id);
On the last line I'm getting the exception:
System.Security.Cryptography.CryptographicException "Invalid algorithm specified."
What am I doing wrong?
UPDATE:
id = 2.16.840.1.101.3.4.2.1
Upvotes: 30
Views: 44968
Reputation: 763
I was able to get around the issue without rebuilding the certificate by loading the cert as exportable and then building the RSA CSP in code. (This is powershell, but C# should be similar):
$Cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($pfxPath, $pfxPassword, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
$rsaCsp = New-Object System.Security.Cryptography.RSACryptoServiceProvider
$rsaParams = $Cert.PrivateKey.ExportParameters($true)
$rsaCsp.ImportParameters($rsaParams)
$signedBytes = $rsaCsp.SignData($toSign,[System.Security.Cryptography.HashAlgorithm]::Create("SHA256"))
Upvotes: 0
Reputation: 11
You need to use
RSA rsaKey = cert.GetRSAPrivateKey();
instead of
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)pXCert.PrivateKey;
And further down the road when you sign the reference specify DigestMethod and SignatureMethod
Reference reference = new Reference();
reference.Uri = "";
// Specify SHA-256 as the hash algorithm
reference.DigestMethod = SignedXml.XmlDsigSHA256Url;
signedXml.AddReference(reference);
// Add the key info to specify the signature method
signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA256Url;
signedXml.ComputeSignature();
Upvotes: 1
Reputation: 2941
Note that I use SHA512 but SHA256 will work with the below examples:
"Invalid algorithm specified" Took me forever to figure out and I tried practically everything.
Step 1 - the certificate has to be SHA512 and use a CSP (Cryptographic Service Provider) that is SHA512 Capable. Here is a list of CSPs and their capabilities. If you look for SHA512 you'll find the "Microsoft Enhanced RSA and AES Cryptographic Provider". By default generating certificates don't use this (at least in Windows) so you have to specify it when you create the certificate.
If you create the certificate with openssl, you can use the option -CSP below to set the correct CSP that will make it work. If you have an existing pfx, you can convert it to a PEM file with openssl, and then back to a pfx to add the option.
Create private key and certificate - this step will ask you questions, state, region etc etc.
openssl req -x509 -nodes -sha512 -newkey rsa:2048 -keyout 512key.pem -out 512cert.pem -days 3650
Create PFX file to import into your certificate store using the Microsoft Enhanced RSA and AES Cryptographic Provider:
openssl pkcs12 –export –in 512cert.pem –inkey 512key.pem –CSP “Microsoft Enhanced RSA and AES Cryptographic Provider” –out 512pfx.pfx
Step 2 : Props to Gonzalo Gallotti for posting the link to the piece of code that helped me. I commented up my code to show what each step is doing. NOTE: this code won't work without a properly generated certificate as described in step 1
public void GetCertificate() {
// Get the Machine Cert Store
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
string alg = CryptoConfig.MapNameToOID("SHA512");
// Open the cert store
store.Open(OpenFlags.ReadWrite);
// Loop through each certificate within the store
foreach (X509Certificate2 myCert in store.Certificates)
{
// Get the certificate we are looking for
if (myCert.IssuerName.Name.Contains("CN=YourSite"))
{
// Check if the certificate has a private key
if (myCert.HasPrivateKey)
{
// Get your custom signature as a string
string mySignature = GetSignatureString();
// Convert signature to byte array
byte[] originalData = Encoding.UTF8.GetBytes(mySignature);
// Create RSA provider from private key
RSACryptoServiceProvider rsaProvider = (RSACryptoServiceProvider)myCert.PrivateKey;
// Sign the signature with SHA512
byte[] signedSignature = signedSignature = rsaProvider.SignData(originalData, alg);
if (rsaProvider.VerifyData(originalData, alg, signedSignature))
{
// Signature is verified Do Stuff
}
else
{
throw new Exception("The data does not match the signature.");
}
}
}
}
}
Upvotes: 12
Reputation: 1
I fixed the issue by upgrading my dependencies.
Instead of relying on the GAC version I was previously using for years, I switched to the latest NuGet packages (v16.8.0) of:
This fixed this issue for us.
Upvotes: 0
Reputation: 2058
Which CSP your certificate is using can be checked with certutil tool (on windows)
certutil yourCertificate.p12
Examples:
Upvotes: 4
Reputation: 1963
You can set the AppContext switches in a web config via appSettings:
<appSettings>
<add key="AppContext.SetSwitch:Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms" value="true" />
<add key="AppContext.SetSwitch:Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms" value="true" />
</appSettings>
Upvotes: 2
Reputation: 311
For dot net framework 4.7.0 or higher is not taking the sha1 so configure the below in application start. it's worked fine for me.
AppContext.SetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", true);
AppContext.SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);
Upvotes: 21
Reputation: 3120
You might have come here while you are migrating your application from .NET Framework 4.7 and earlier versions to 4.7.1 or later versions.
If you are getting the exception System.Security.Cryptography.CryptographicException: Invalid algorithm specified.
, the reason is that default SignedXML and SignedXMS algorithms changed to SHA256 for applications that target the .NET Framework 4.7.1 and later versions (from Microsoft .NET migration guide)
In that guide you'll also find the solution:
For applications that target the .NET Framework 4.7.1 and later versions, if the use of SHA256 is undesirable, you can restore the default to SHA1 by adding the following configuration switch to the runtime section of your app config file:
<AppContextSwitchOverrides value="Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms=true; Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms=true" />
But this may not always work, especially for web applications, as you can read in this blog post, fortunately along with the answer as well. It is only necessary to add some lines in the Application_Start
protected void Application_Start(object sender, EventArgs e)
{
[...]
AppContext.SetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", true);
AppContext.SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);
}
Upvotes: 15
Reputation: 3619
Having a similar issue but just resolved it. If you are not using X509 but just the plain RSACryptoServiceProvider to get the keys, then only SHA1 is supported.
Upvotes: 3
Reputation: 140983
There is no issue with .NET code or the CSP code you provided.
Your problem is that CSP just doesn’t support SHA 256. You can get further information here
Upvotes: 16