Windows server 2012 "Invalid algorithm specified." - c#

When I try to sign data it always says Invalid algorithm specified. I am using following code:
Here is my Certificate Details.
Please Help me, Thanks.

Instead of
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PrivateKey;
return csp.SignHash(hash, CryptoConfig.MapNameToOid("SHA256"));
use
using (RSA privateKey = cert.GetRSAPrivateKey())
{
return privateKey.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
(Requires .NET 4.6+)
The problem is your RSACryptoServiceProvider object is using the CAPI PROV_RSA_FULL provider, which predates SHA-2. SHA-2 signatures (from the software provider) require the key be in PROV_RSA_AES, a fact mostly controlled by the key creation / PFX import.
There's a workaround you can do for rebinding the provider type to still use the soft-deprecated RSACryptoServiceProvider, but particularly in code like this (where the object does not leave the method) you should switch to using cert.GetRSAPrivateKey() and avoid casting the returned RSA object beyond the algorithm base class.

Related

How to persist private key of RSAParameter in C#/.net

Starting with .Net 4.7.2 (.Net Standard 2.0) it's possible to create self-signed certificates and certificate signing requests with C#/.Net only, see the MS Documentation.
While it's straight forward to create a self-signed certificate which will assert HasPrivateKey (you just call CreateSelfSigned(notBefore, notAfter)) I'm having a hard time to figure out how to get hold of the private key in general, e.g. if I want to create a certificate signed by a CA and then want to persist the certificate as a PFX file or want to persist the private key in a .PEM file or want to store it in the MS certificate store together with the private key, or when I just want to have in memory and also assert HasPrivateKey.
What I do have is a 'RSAParameters' instance which is in possession of the relevant private information, but I failed to figure out how to (easily) use that for the purpose in question (create a PFX file or PEM file or MS Certificate Store entry) without having to read through all the relevant RFCs and write a program for that on my own. (That RSAParameter instance contains the D, Exponent and Modulus, so I could try to patch this together (with the help of this answer, hopefully), but I was hoping for a C# method which will perform these tasks for me (which I could not find) by now).
Of course the idea is to do that with .Net functionality alone, as well.
Every hint on how to achieve this is appreciated.
If you only have Modulus, Exponent, and D you first have to recover the CRT parameters (P, Q, DP, DQ, InverseQ).
As your other questions, you're mainly missing the cert.CopyWithPrivateKey(key) extension methods and rsa.ImportParameters(RSAParameters):
if I want to create a certificate signed by a CA and then want to persist the certificate as a PFX file
using (RSA rsa = RSA.Create())
{
rsa.ImportParameters(rsaParameters);
using (X509Certificate2 caSigned = GetCASignedCert(rsa))
using (X509Certificate2 withKey = caSigned.CopyWithPrivateKey(rsa))
{
File.WriteAllBytes("some.pfx", withKey.Export(X509ContentType.Pkcs12, "and a password"));
}
}
or want to persist the private key in a .PEM file
This one is available in .NET Core 3.0 daily builds:
RSAParameters rsaParameters = default(RSAParameters);
using (StreamWriter writer = new StreamWriter("rsa.key"))
using (RSA rsa = RSA.Create())
{
rsa.ImportParameters(rsaParameters);
writer.WriteLine("-----BEGIN RSA PRIVATE KEY-----");
writer.WriteLine(
Convert.ToBase64String(
rsa.ExportRSAPrivateKey(),
Base64FormattingOptions.InsertLineBreaks));
writer.WriteLine("-----END RSA PRIVATE KEY-----");
}
PKCS#8 and encrypted PKCS#8 are also available.
On existing versions this requires using the RSAParameters and a ITU-T X.690 DER encoder.
or want to store it in the MS certificate store together with the private key
using (RSA rsa = RSA.Create())
{
rsa.ImportParameters(rsaParameters);
using (X509Certificate2 caSigned = GetCASignedCert(rsa))
using (X509Certificate2 withKey = caSigned.CopyWithPrivateKey(rsa))
using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
X509Certificate2 persisted = new X509Certificate2(
withKey.Export(X509ContentType.Pkcs12, ""),
"",
X509KeyStorageFlags.PersistKeySet);
using (persisted)
{
store.Open(OpenFlags.ReadWrite);
store.Add(persisted);
}
}
}
or when I just want to have in memory and also assert HasPrivateKey.
using (RSA rsa = RSA.Create())
{
rsa.ImportParameters(rsaParameters);
using (X509Certificate2 caSigned = GetCASignedCert(rsa))
{
// Yes, this value can outlive both usings
return caSigned.CopyWithPrivateKey(rsa);
}
}
If you are using a certificate hardware security module (HSM) such as a USB key, for example, it is not possible to "get" the private key because the HSM only provides an interface for using the private key. This is for security as once a private key is in a file or memory it is potentially obtainable by a third party.
Also .NET has, historically, not presented a flexible enough interface, although it is improving. As such a many software vendors use the more complete and well-maintained, Bouncy Castle API (http://www.bouncycastle.org/csharp/) and you will find a lot of documentation around the web. Generally if .NET can't do it - bouncy castle will. Ironically an HSM requires .NET crypto access it's private key functionality on windows, but you usually encapsulate that somehow.
It is a steep learning curve using crypto APIs in general and it's unlikely that you will get much assistance without having a code example you want to make work.

Getting Exception "Invalid Provider type specified" or "Key does not exist" while getting Private key from X509Certificate2 Occasionally

I am getting one of the following Exceptions while trying to get a private key from X509Certificate2 certificate:
System.Security.Cryptography.CryptographicException: Invalid provider type specified.
OR
System.Security.Cryptography.CryptographicException: Key does not exist at the following line of code: RSACryptoServiceProvider rsaKey = (RSACryptoServiceProvider)digiSignCert.PrivateKey;
Stacktrace:
System.Security.Cryptography.CryptographicException: Key does not exist. at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle) at System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair() at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize) at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey() at Api.CertificateUtil.GetSignedXml(String xml, X509Certificate2 privateCert)
Code:
public static RSACryptoServiceProvider rsaKey = null;
public X509Certificate2 _PrivateCert;
public APISearch()
{
byte[] privateCert = null;//We get the actual certificate file data here
GetPrivateCerificate(privateCert, "abc#123");
GetSignedXml(_PrivateCert);
}
public void GetPrivateCerificate(byte[] privateCert, string pwd)
{
_PrivateCert = new X509Certificate2(privateCert, pwd, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
}
public void GetSignedXml(X509Certificate2 privateCert)
{
rsaKey = (RSACryptoServiceProvider)privateCert.PrivateKey; //Occassional Exception
}
Expected result: (RSACryptoServiceProvider)privateCert.PrivateKey should always produce a private key.
Actual result: Sometimes the aforementioned exceptions are thrown at this line:
rsaKey = (RSACryptoServiceProvider)privateCert.PrivateKey;
and sometimes the private key is successfully being fetched from the certificate file. As of now, we have been unable to track the pattern of this problem.
RSACryptoServiceProvider is a type that performs RSA via the Window Cryptographic API (CAPI) library. When .NET was first created CAPI was new and always the right answer (on Windows). Starting in Windows Vista there was a new library: Cryptography: Next Generation (CNG). CNG, for compatibility, understands how to work with CAPI. But CAPI can't "be CAPI" and "understand CNG". The exceptions you are seeing are when a PFX indicated that the private keys should be stored via CNG (or an in-store cert indicates that it's private keys are stored via CNG).
When .NET Framework was adding RSACng it was decided that too many people had already written the line (RSACryptoServiceProvider)cert.PrivateKey, so that property can't ever return an RSACng instance. Instead, in .NET 4.6 new (extension) methods were made: cert.GetRSAPublicKey() and cert.GetRSAPrivateKey(), they return RSA instead of AsymmetricAlgorithm. Also in .NET 4.6 the RSA base class was enhanced to have the Sign/Verify and Encrypt/Decrypt operations moved down (though with different signatures, since RSA has gained new options since CAPI was written).
Expected result: (RSACryptoServiceProvider)privateCert.PrivateKey should always produce a private key.
The actual truth is cert.PrivateKey (and cert.PublicKey.Key) is/are soft-deprecated. You shouldn't call it/them anymore. RSA (4.6), ECDSA (4.6.1) and DSA (4.6.2) all have Get[Algorithm]{Public|Private}Key methods.
(RSACryptoServiceProvider)cert.PrivateKey => cert.GetRSAPrivateKey()
rsaCSP.Encrypt(data, false) => rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1)
rsaCSP.Encrypt(data, true) => rsa.Encrypt(data, RSAEncryptionPadding.OaepSHA1)
rsaCSP.SignData(data, "SHA256") => rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)
Similar for Decrypt, SignHash, VerifyData, VerifyHash; and similar for ECDsa and DSA.
Finally, please don't hard-cast the return value of those methods, it changes as it needs to... on Windows it can return either RSACng or RSACryptoServiceProvider, on Linux (.NET Core) it currently returns RSAOpenSsl, and on macOS (.NET Core) it returns an uncastable object.

Update x509Certificate2 with correct Cryptographic Service Provider in Bouncy Castle

I am creating x509Certificate2 using CertificateGenerator.GenerateCertificate from this blog post of Wiktor Zychla (http://www.wiktorzychla.com/2012/12/how-to-create-x509certificate2.html)
Bouncy Castle crypto library is used to generate the certificate file. I need stronger signature algorithm to be used, so instead of SHA1withRSA (one in example) I am using SHA256withRSA.
Certificate is generated and exported successfully to .pfx file. When using the certificate later on I get error Invalid algorithm specified.
When run certutil -dump mycert.pfx, I see incorrect Cryptographic Service Provider (CSP) is set: Microsoft Base Cryptographic Provider v1.0
...
---------------- End Nesting Level 1 ----------------
Provider = Microsoft Base Cryptographic Provider v1.0
...
How can I tell Bouncy Castle API to use different CSP? The Microsoft Enhanced RSA and AES Cryptographic Provider, that actually can deal with SHA256withRSA.
There is very little resources on Bouncy Castle and C#, so any link to some documentation or related examples would be greatly appreciated.
List of CryptoAPI CSPs and algorithms, they support:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb931357(v=vs.85).aspx
The easiest way is to specify the CSP when you are importing the generated pfx. You can use this command
certutil -importPFX -csp "Microsoft Enhanced RSA and AES Cryptographic Provider" -v c:\yourpfx.pfx AT_KEYEXCHANGE,NoExport,NoProtect
which will
import into LocalMachine\My
set CSP to Microsoft Enhanced RSA and AES Cryptographic Provider
set private key usage to Exchange
set private key as non-exportable
set private key with no additional (password) protection
The CSP is windows specific field in PKCS#12 (PFX) and no one except windows is setting this. If you are using PFX from file new X509Certificate2(filename) then you have to change the private key. Convert PrivateKey property to RSACryptoServiceProvider and modify CspParameters (I don't have a snippet right now). Then set the modified RSACryptoServiceProvider back to PrivateKey property.
------- Edit
Here is the sample code that changes CSP on PFX read from file
// need to set exportable flag to be able to ... export private key
X509Certificate2 cert = new X509Certificate2(#"d:\test.pfx", "a", X509KeyStorageFlags.Exportable);
var privKey = cert.PrivateKey as RSACryptoServiceProvider;
// will be needed later
var exported = privKey.ToXmlString(true);
// change CSP
var cspParams = new CspParameters()
{
ProviderType = 24,
ProviderName = "Microsoft Enhanced RSA and AES Cryptographic Provider"
};
// create new PrivateKey from CspParameters and exported privkey
var newPrivKey = new RSACryptoServiceProvider(cspParams);
newPrivKey.FromXmlString(exported);
// Assign edited private key back
cert.PrivateKey = newPrivKey;
// export as PKCS#12/PFX
var bytes = cert.Export(X509ContentType.Pfx, "a");

Implementing Hybrid Encryption?

I already have an asymmetric algorithm implemented in an MVC C# Application, however I would like to modify the encryption method so that I make use of both symmetric and asymmetric encryption (AKA Hybrid encryption). Any idea how I can do this?
Asymmetric encrypt:
public string AsymmEncrypt(int accId, string input, string publickey)
{
Account a = new UserRepository().GetAccountById(accId);
RSACryptoServiceProvider myAlg = new RSACryptoServiceProvider();
CspParameters cspParams = new CspParameters();
publickey = new UserRepository().PublicKeyByAccountId(accId);
cspParams.KeyContainerName = publickey;
myAlg = new RSACryptoServiceProvider(cspParams);
byte[] cipher = myAlg.Encrypt(UTF8Encoding.UTF8.GetBytes(input), true);
return Convert.ToBase64String(cipher);
}
Asymmetric decrypt:
public string AsymmDecrypt(int accId, string input, string privatekey)
{
Account a = new UserRepository().GetAccountById(accId);
RSACryptoServiceProvider myAlg = new RSACryptoServiceProvider();
CspParameters cspParams = new CspParameters();
privatekey = new UserRepository().PrivateKeyByAccountId(accId);
byte[] cipher = myAlg.Decrypt(Convert.FromBase64String(input), true);
return UTF8Encoding.UTF8.GetString(cipher);
}
You should probably not try to reinvent the wheel here. The System.Security.Cryptography namespace in .net alrady provides a large array of cryptography functionality that is quite well vetted. Don't try to use your own Asymmetric functions to accomplish this.
If you want to do private key distribution through public key encryption, you should use something like RSAPKCS1KeyExchangeFormatter or maybe even RSAOAEPKeyExchangeFormatter if you have the flexibility to support PKCS#1v2
I would suggest reading how SSL or OpenPGP are implemented.
I'm not sure what part you are struggling with.
In short, the asymmetric algorithm is used for symmetric key exchange.
The symmetric algorithm is used for the bulk data (stream/block) crypto. You won't get it done with simply modifying your 2 functions, you will need to implement a handshake and key exchange.
Since you have an MVC.NET app, you can host it within a web server and gain HTTPS/SSL transport. You can also do the same with WCF. Any reason why aren't using what is provided by the underlying transport? You can even configure your application (web.config) to require client certificates.
PS: I agree about not re-inventing the wheel, even Microsoft's article that Erik linked to provides a warning about it.
Caution We recommend that you do not attempt to create your own key exchange method from the basic functionality provided, because many details of the operation must be performed carefully in order for the key exchange to be successful.

Loading the Jira Public Certificate in .Net from a string (how to convert ASN.1 encoded SubjectPublicKeyInfo to X509 Cert in .Net)

I am building an oauth 1.0a service that will be consumed by a gadget within Jira, it's a .Net 3.5 Application written in C#.
Jira makes requests to this service using the RSA-SHA1 signature method, which means to verify the signature of the request I need create an X509Certificate instance form their public cert.
Within the Jira application you can get the public cert by going to the consumer info screen (which also has the consumer key for Jira etc.) and it presents the public key in this format:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCObJRTGSZbAo
jRkvKmm0cwFXnKcPMfR4t/sghvLe/+QVs6TJOz5cUh5UokSqyz
VeMsL0jomP18ZcR3SPcIFT7xtOGQjLwLk7ghfYSsxjTGs9VxsC
/PQk5OQRP3v43IxFNF3M2SYhFWJZTOnqrab5AsMh2Kxdv+D69D
CINXCu5ltQIDAQAB
Looking at the Jira code which generates this key I can see it's (supposedly) PEM encoded without the BEGIN/END certificate header/footer.
RSAKeys.toPemEncoding(consumer.getPublicKey())
RSAKeys is an open source class found here:
https://studio.atlassian.com/source/browse/OAUTH/trunk/api/src/main/java/com/atlassian/oauth/util/RSAKeys.java?r=HEAD
I wish to load this public cert (key) into an X509Certificate instance within .Net, but my attempts so far have failed. Here's the code I have:
static readonly Regex stripRegex = new Regex("-----[A-Z ]*-----");
public string ConvertFromOpenSsl(string key)
{
return stripRegex.Replace(key, "").Replace("\r", "").Replace("\n", "");
}
public X509Certificate2 GetConsumerCertificate(IConsumer consumer)
{
string cert =
#"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCObJRTGSZbAo
jRkvKmm0cwFXnKcPMfR4t/sghvLe/+QVs6TJOz5cUh5UokSqyz
VeMsL0jomP18ZcR3SPcIFT7xtOGQjLwLk7ghfYSsxjTGs9VxsC
/PQk5OQRP3v43IxFNF3M2SYhFWJZTOnqrab5AsMh2Kxdv+D69D
CINXCu5ltQIDAQAB";
string converted = ConvertFromOpenSsl(cert);
var bytes = Convert.FromBase64String(converted);
var cert = new X509Certificate2(bytes); // throws here
But on the last line of code I have an exception thrown:
System.Security.Cryptography.CryptographicException: Cannot find the requested object.
at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
at System.Security.Cryptography.X509Certificates.X509Utils._QueryCertBlobType(Byte[] rawData)
at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromBlob(Byte[] rawData, Object password, X509KeyStorageFlags keyStorageFlags)
at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(Byte[] data)
at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(Byte[] rawData)
I'm pretty sure I am missing something elementary, but I can think what it is.
UPDATE
OK, on further investigation it appears that this is a SubjectPublicKeyInfo serialization of the public key, so it's ASN.1, base 64 encoded (162 bytes unencoded), which is the default output from Java using java.security.PublicKey.getEncoded().
So given all that - is there any easy way to create an X509Certificate2 instance wrapping this public key - or is additional metadata required beyond the public key to create an x509Certificate2 instance?
Jira should provide you with a Certificate (not just a public key).
Typically the Java world will give a base64 encoded or PEM certificate. X509Certificate2 from .Net can automatically .Load a base64, PEM or binary certificate.
you can generate your XML RSA certificate via .NET using RSACryptoServiceProvider. This will give you XML (FromXmlString method), the public key then needs to be encoded, for example by using this service:
https://superdry.apphb.com/tools/online-rsa-key-converter
and then used to create application link to JIRA.
The private key in XML form you got previously, can be used for signing .NET app requests directly.
I personally used DonNetAuth library for signing, exchannging tokens, etc and it works for me. The only bug I encountered was regarding jql queries, where the signing needed a bit of tweaking to work correctly. Here is the link:
http://samondotnet.blogspot.sk/2012/12/introduction-to-dotnetauth.html
Additionally see this link:
https://answers.atlassian.com/questions/172760/is-there-any-jira-oauth-implementation-example-in-net
Hope this helps.

Categories