Difference between MakeCert and OpenSSL wrt C# SslStream - c#

I am attempting to create a TCP connection on TLS encryption in C#, using an OpenSSL generated cert.
The SslStream sample code given here seems to work using a cert created with windows makecert.exe, which comes out as a .cer file. It does not work with a cert created through OpenSSL, which is a .pem file, throwing excpetion: "The server mode SSL must use a certificate with the associated private key."
Both cert files are being parsed successfully into X509Certificate objects as I can see their issuers and subjects and other properties, and note that the "HasPrivateKey" property is false "PrivateKey" property is null on both of them.
I found this which says that you need to combine the (OpenSSL) certificate and private key into one PKCS12 package. Yet a PKCS12 package is the file format .pfx -- why does an OpenSSL cert have to be combined with a private key into a pfx, but a makecert-generated .cer file does not need to be a pfx (and is not PKCS12)?
Also, what is different about the OpenSSL certificate in the first place that makes it throw this exception about a missing private key, when the MakeCert certificate also does not have a private key? Or to put another way, why does the MakeCert certificate not seem to require a private key, like the OpenSSL cert?

Someone on IRC suggested to me that it might be that makecert is putting credentials in the windows Certificate Store. I didn't expect that would be the case since we are exporting files, but I moved my SslStream prototype (with makecert certificates) to a different machine (ie not the one where makecert was run) and I got the same error "The server mode SSL must use a certificate with the associated private key."
So officially, the answer is "makecert puts the key into your cert store, and when you run AuthenticateAsClient, in the background it is pulling the private key out of there. This is why the makecert certificate works and the OpenSSL cert doesn't, even though they are almost identical.
Taking it a step further, to get the thing working with OpenSSL certs, I'm sure the advice I linked in the question about combining the public and private keys into a PKCS12 (.pfx) file would have worked, however I wanted to load/combine them on the fly. I found this codeproject article which contains sample code that does all the dirty work of decoding the RSA private key, making it into an RSACryptoProvider, and attaching that object to the X509Certificate2.PrivateKey property. After doing this and providing paths to the seperate cert and private key files generated by OpenSSL, everything works great!

Related

IdentityServer4: PFX certificate for production use?

In the sample code, there is a PFX certificate file called "idsrv3test.pfx"
How do I configure a PFX certificate for production use? Can I use a self-sign PFX?
Ref: ASP.NET Core Authentication with IdentityServer4
As mentioned in my previous post, it’s possible to create self-signed certificates for testing this out with the makecert and pvk2pfx command line tools (which should be on the path in a Visual Studio Developer Command prompt).
makecert -n "CN=AuthSample" -a sha256 -sv IdentityServer4Auth.pvk -r IdentityServer4Auth.cer
This will create a new self-signed test certificate with its public key in IdentityServer4Auth.cer and it’s private key in IdentityServer4Auth.pvk.
pvk2pfx -pvk IdentityServer4Auth.pvk -spc IdentityServer4Auth.cer -pfx IdentityServer4Auth.pfx
This will combine the pvk and cer files into a single pfx file containing both the public and private keys for the certificate. Our app will use the private key from the pfx to sign tokens. Make sure to protect this file. The .cer file can be shared with other services for the purpose of signature validation.
From a security stand point pay special attention to:
Note that you should not load the certificate from the app path in production; there are other AddSigningCredential overloads that can be used to load the certificate from the machine’s certificate store.
In a production environment you should be using CertMgr.msc (or the platform equivalent) to store your certificate with private key in the local computer's certificate store (not any particular user's certificate store). When importing the certificate with CertMgr.msc be sure to tick "Mark this certificate as exportable" otherwise the private key will not be accessible to your service to sign data with. This is just like installing SSL/TLS Certificates for IIS consumption.

Sign a string with RSA SHA 256 with ONLY the Private Key as a String .NET

We're doing an integration with Google via RESTful API and I need to sign a payload of a JWT with RSA-SHA256 and all I have is a Private Key as a string, that looks like
-----BEGIN PRIVATE KEY-----
MIIEvgIBfADANBg9qhkdsiG9w0BAQEFAASCBKgw
......
-----END PRIVATE KEY-----
I've been looking everywhere for a solution, but everyone's talking about X509Certificate2 which needs a p12 file, or some Certificates. Now I don't have either of those, I only have a string that is the private key.
Google recommends https://jwt.io/ and it works with the key string, but well, I need my CODE to do it. There are 3 libraries listed in the page:
One of them belongs to Microsoft and has absolutely no documentation.
Another one is using X509Certificate2 or specifically for string keys only has a link to this place http://www.donaldsbaconbytes.com/2016/08/create-jwt-with-a-private-rsa-key/ which uses some Certificate files I don't have, making it a wrong and unhelpful link to begin with.
Same creators of #2 have another lib, but it's for phones and it actually has a method for RSA256 signing which takes private key as a string of exactly my format.
So my question is - is there some working solution for my case? Because I can't believe that no one else has integrations with Google.
I know of a way to convert this to a pfx that you can use in C#.
First, install OpenSSL if you haven't already. (You could get the latest "Win64 OpenSSL v#.#.#x Light" from http://slproweb.com/products/Win32OpenSSL.html, though I take no responsibility for this source.)
You may want to add "C:\Program Files\OpenSSL\bin\" to your account's path variable, so that "openssl.exe" can be used from anywhere in the command line.
In a command window, navigate to where your private key file is. Let's call it "priv.key". Issue the following two commands, ignoring the comments:
# Create public self-signed certificate CRT file (with 7000yr validity) from private key (may leave all info empty)
openssl req -x509 -sha256 -new -key priv.pem -out pub.crt -days 2555000
# Combine into PFX container (encrypted with AES-256) (certificate needs to be included for .NET to read successfully)
openssl pkcs12 -export -aes256 -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider" -inkey priv.pem -in pub.crt -out priv.pfx
Note that on the last command, I am creating an encrypted pfx (this is optional - I believe you could replace -aes256 by -nodes to skip it), and I am configuring the cryptographic provider such that the key can be used for SHA256 operations without C# complaining.
You now have a pfx file that you can load using the constructor of X509Certificate2.
I tend to use this cheat sheet for common OpenSSL commands:
https://www.sslshopper.com/article-most-common-openssl-commands.html

C# - How to merge private key with certificate

Is there a way to add private key to certificate? API of Certificate authority app returns two base64 strings (one with private key, one with certificate without private part). I want to save the Resulting certificate onto smart card.
There are some answers but there are missing libraries or they don't work at all.
What about building an X509Certificate2 from it, using e.g. Associate a private key with the X509Certificate2 class in .net
then, dump the certificate to PFX using certificate.Export(X509ContentType.Pkcs12,"password"), and dumping the byte[] to disk, and finally, launch:
certutil -importPFX <pfxFile> -p password -csp "Microsoft Smart Card Key Storage Provider" ...

"Invalid provider type specified" CryptographicException when trying to load private key of certificate

I'm trying to read the private key of a certificate which has been shared with me by a third-party service provider, so I can use it to encrypt some XML before sending it to them over the wire. I'm doing so programmatically in C#, but I think this is a permissions or misconfiguration issue, so I'll focus on the facts which seem to be most relevant:
I don't think this issue is code-related; my code works on other computers, and the issue affects sample code from Microsoft.
The certificate was provided as a PFX file and is just for test purposes, so it also includes a dummy certification authority.
Using MMC.exe, I can import the certificate into the personal store for the local machine, before granting permissions on the private key to all relevant accounts, and dragging and dropping the certification authority into the Trusted Root Certification Authorities.
Using C#, I can load the certificate (identified by its thumbprint) and verify that it has a private key using X509Certificate2.HasPrivateKey. However, trying to read the key causes an error. In .NET a CryptographicException is thrown with the message "Invalid provider type specified" upon trying to access the property X509Certificate2.PrivateKey. In Win32, calling the method CryptAcquireCertificatePrivateKey returns the equivalent HRESULT, NTE_BAD_PROV_TYPE.
This is the same exception which also occurs when using two of Microsoft's own code samples to read the private key of the certificate.
Installing the same certificate in the equivalent store for the current user, instead of the local machine, allows the private key to be successfully loaded.
I'm on Windows 8.1 with local administrator rights, and I've tried running my code in both normal and elevated modes. Colleagues on Windows 7 and Windows 8 have been able to load the key from the local machine store for the same certificate.
I can successfully read the private key of the self-signed IIS test certificate, which is in the same store location.
I am already targeting .NET 4.5 (this error has been reported with some older versions of the framework).
I don't think this is a problem with certificate templates, because I would expect that to affect both the local machine and current-user stores equally?
Unlike my colleagues, I have made multiple previous attempts to uninstall and re-install the certificate in various ways, including via IIS Manager and also including an older certificate from the same issuer. I can't see any traces of old or duplicate certificates in MMC. However, I do have many private key files of identical size which, based on the last-write time, must have been left behind after my various installation attempts. These are found at the following locations, for the local machine and current user stores respectively:
c:\ProgramData\Microsoft\Crypto\RSA\MachineKeys
c:\Users\\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-[rest of user ID]
So, can anyone please advise whether:
It's a good idea to uninstall the certificate using MMC, delete all those files which look like orphaned private keys, and then re-install the certificate and try again?
There any other files which I should try to manually delete?
There's anything else I should try?
UPDATE - Added a code sample showing an attempt to read a private key:
static void Main()
{
// Exception occurs when trying to read the private key after loading certificate from here:
X509Store store = new X509Store("MY", StoreLocation.LocalMachine);
// Exception does not occur if certificate was installed to, and loaded from, here:
//X509Store store = new X509Store("MY", StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
X509Certificate2Collection scollection = X509Certificate2UI.SelectFromCollection(fcollection, "Test Certificate Select", "Select a certificate from the following list to get information on that certificate", X509SelectionFlag.MultiSelection);
Console.WriteLine("Number of certificates: {0}{1}", scollection.Count, Environment.NewLine);
foreach (X509Certificate2 x509 in scollection)
{
try
{
Console.WriteLine("Private Key: {0}", x509.HasPrivateKey ? x509.PrivateKey.ToXmlString(false) : "[N/A]");
x509.Reset();
}
catch (CryptographicException ex)
{
Console.WriteLine(ex.Message);
}
}
store.Close();
Console.ReadLine();
}
I had the same problem on Windows 8 and Server 2012/2012 R2 with two new certificates I recently received. On Windows 10, the problem no longer occurs (but that does not help me, as the code manipulating the certificate is used on a server). While the solution of Joe Strommen in principle works, the different private key model would require massive change to the code using the certificates. I find that a better solution is to convert the private key from CNG to RSA, as explained by Remy Blok here.
Remy uses OpenSSL and two older tools to accomplish the private key conversion, we wanted to automate it and developed an OpenSSL-only solution. Given MYCERT.pfx with private key password MYPWD in CNG format, these are the steps to get a new CONVERTED.pfx with private key in RSA format and same password:
Extract public keys, full certificate chain:
OpenSSL pkcs12 -in "MYCERT.pfx" -nokeys -out "MYCERT.cer" -passin "pass:MYPWD"
Extract private key:
OpenSSL pkcs12 -in "MYCERT.pfx" -nocerts -out "MYCERT.pem" -passin "pass:MYPWD" -passout "pass:MYPWD"
Convert private key to RSA format:
OpenSSL rsa -inform PEM -in "MYCERT.pem" -out "MYCERT.rsa" -passin "pass:MYPWD" -passout "pass:MYPWD"
Merge public keys with RSA private key to new PFX:
OpenSSL pkcs12 -export -in "MYCERT.cer" -inkey "MYCERT.rsa" -out "CONVERTED.pfx" -passin "pass:MYPWD" -passout "pass:MYPWD"
If you load the converted pfx or import it in the Windows certificate store instead of the CNG format pfx, the problem goes away and the C# code does not need to change.
One additional gotcha that I encountered when automating this: we use long generated passwords for the private key and the password may contain ". For the OpenSSL command line, " characters inside the password must be escaped as "".
In my case, I was trying to use a self-signed certificate with PowerShell's New-SelfSignedCertificate command. By default, it will generate a certificate using the CNG (Crypto-Next Generation) API instead of the older/classic crypto CAPI. Some older pieces of code will have trouble with this; in my case it was an older version of the IdentityServer STS provider.
By adding this at the end of my New-SelfSignedCertificate command, I got past the issue:
-KeySpec KeyExchange
Reference on the switch for the powershell command:
https://learn.microsoft.com/en-us/powershell/module/pkiclient/new-selfsignedcertificate?view=win10-ps
Here is another reason that this can happen,
this was a weird issue and after struggling for a day I solved the issue.
As an experiment I changed permission for "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys" folder which holds private key data for certificates using Machine key store. When you change permission for this folder all privatekeys shows up as "Microsoft Software KSP provider" which is not the provider (in my case they are supposed to be " Microsoft RSA Schannel Cryptographic Provider").
Solution: Reset permissions to Machinekeys folder
Original permission for this folder can be found in here. In my case I have changed permission for "Everyone", gave read permissions where it removed "Special permissions" tick. So I checked with one of my team member (Right click folder> Properties > Security > Advanced > select "Everyone" > Edit > Click "Advanced settings" in permission check box list
Hope this will save someone's day!
Here is where I found the answer source, credit goes to him for documenting this.
In my case, the following code worked fine in localhost (both NET 3.5 and NET 4.7):
var certificate = new X509Certificate2(certificateBytes, password);
string xml = "....";
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.PreserveWhitespace = true;
xmlDocument.LoadXml(xml);
SignedXml signedXml = new SignedXml(xmlDocument);
signedXml.SigningKey = certificate.PrivateKey;
//etc...
But it failed when deployed to an Azure Web App, at certificate.PrivateKey
It worked by changing the code as follows:
var certificate = new X509Certificate2(certificateBytes, password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
//^ Here
string xml = "....";
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.PreserveWhitespace = true;
xmlDocument.LoadXml(xml);
SignedXml signedXml = new SignedXml(xmlDocument);
signedXml.SigningKey = certificate.GetRSAPrivateKey();
// ^ Here too
//etc...
A whole day of work lost thanks to Microsoft Azure, once again in my life.
The link to Alejandro's blog is key.
I believe this is because the certificate is stored on your machine with the CNG ("Crypto Next-Generation") API. The old .NET API is not compatible with it, so it doesn't work.
You can use the Security.Cryptography wrapper for this API (available on Codeplex). This adds extension methods to X509Certificate/X509Certificate2, so your code will look something like:
using Security.Cryptography.X509Certificates; // Get extension methods
X509Certificate cert; // Populate from somewhere else...
if (cert.HasCngKey())
{
var privateKey = cert.GetCngPrivateKey();
}
else
{
var privateKey = cert.PrivateKey;
}
Unfortunately the object model for CNG private keys is quite a bit different. I'm not sure if you can export them to XML like in your original code sample...in my case I just needed to sign some data with the private key.
What worked for me:
IIS / Application pool / Load User Profile = true
As many other answers have pointed out, this issue arises when the private key is a Windows Cryptography: Next Generation (CNG) key instead of a "classic" Windows Cryptographic API (CAPI) key.
Beginning with .NET Framework 4.6 the private key (assuming it's an RSA key) can be accessed via an extension method on X509Certificate2: cert.GetRSAPrivateKey().
When the private key is held by CNG the GetRSAPrivateKey extension method will return an RSACng object (new to the framework in 4.6). Because CNG has a pass-through to read older CAPI software keys, GetRSAPrivateKey will usually return an RSACng even for a CAPI key; but if CNG can't load it (e.g. it's an HSM key with no CNG driver) then GetRSAPrivateKey will return an RSACryptoServiceProvider.
Note that the return type for GetRSAPrivateKey is RSA. Beginning with .NET Framework v4.6 you shouldn't need to cast beyond RSA for standard operations; the only reason to use RSACng or RSACryptoServiceProvider is when you need to interop with programs or libraries that use the NCRYPT_KEY_HANDLE or the key identifier (or opening a persisted key by name). (.NET Framework v4.6 had a lot of places that still cast the input object to RSACryptoServiceProvider, but those were all eliminated by 4.6.2 (of course, that's more than 2 years ago at this point)).
ECDSA certificate support was added in 4.6.1 via a GetECDsaPrivateKey extension method, and DSA was upgraded in 4.6.2 via GetDSAPrivateKey.
On .NET Core the return value from Get[Algorithm]PrivateKey changes depending on the OS. For RSA it's RSACng/RSACryptoServiceProvider on Windows, RSAOpenSsl on Linux (or any UNIX-like OS except macOS), and a non-public type on macOS (meaning you can't cast it beyond RSA).
Using Visual Studio 2019 and IISExpress, I was able to correct this problem by removing the following flag when loading the .pfx|.p12 file:
X509KeyStorageFlags.MachineKeySet
Before:
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable
After:
X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable
Normally, I'll load the cert this way:
var myCert = new X509Certificate2("mykey.pfx", "mypassword", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
This does NOT raise an Exception, instead the Exception is raised when attempting to use the certificate (or in my case, when trying to obtain the .PrivateKey). I've found this issue can be caused when the invoking user has insufficient permissions.
Since I rely on the MachineKeySet flag for some environments, my current solution is to swallow the Exception, change the flags, try again. :/
A more organic solution would be to test the permissions level and set this flag dynamically, however I'm not aware of a simple way to do this, hence the fallback.
NOTE: The .pfx file I'm using was created from a website that uses a JavaScript library (digitalbazaar/forge) which does NOT use CNG (Cryptography Next Generation) Key Storage Providers. here are many common causes for this same error (the most common fixes related to CNG extensions, which have -- unfortunately even changed namespaces in .NET versions), which throw the same error. Microsoft should ultimately be more verbose when throwing these types of errors.
I also had this issue and after attempting the suggestions in this post without success. I was able to resolve my issue by reloading the certificate with the Digicert certificate utility https://www.digicert.com/util/. This allows one to select the provider to load the certificate into. In my case loading the certificate into the Microsoft RSA Schannel Cryptographic Provider provider where I had expected it to be in the first place resolved the issue.
Powershell version of the answer from #berend-engelbrecht, assuming openssl installed via chocolatey
function Fix-Certificates($certPasswordPlain)
{
$certs = Get-ChildItem -path "*.pfx" -Exclude "*.converted.pfx"
$certs | ForEach-Object{
$certFile = $_
$shortName = [io.path]::GetFileNameWithoutExtension($certFile.Name)
Write-Host "Importing $shortName"
$finalPfx = "$shortName.converted.pfx"
Set-Alias openssl "C:\Program Files\OpenSSL\bin\openssl.exe"
# Extract public key
OpenSSL pkcs12 -in $certFile.Fullname -nokeys -out "$shortName.cer" -passin "pass:$certPasswordPlain"
# Extract private key
OpenSSL pkcs12 -in $certFile.Fullname -nocerts -out "$shortName.pem" -passin "pass:$certPasswordPlain" -passout "pass:$certPasswordPlain"
# Convert private key to RSA format
OpenSSL rsa -inform PEM -in "$shortName.pem" -out "$shortName.rsa" -passin "pass:$certPasswordPlain" -passout "pass:$certPasswordPlain" 2>$null
# Merge public keys with RSA private key to new PFX
OpenSSL pkcs12 -export -in "$shortName.cer" -inkey "$shortName.rsa" -out $finalPfx -passin "pass:$certPasswordPlain" -passout "pass:$certPasswordPlain"
# Clean up
Remove-Item "$shortName.pem"
Remove-Item "$shortName.cer"
Remove-Item "$shortName.rsa"
Write-Host "$finalPfx created"
}
}
# Execute in cert folder
Fix-Certificates password
I faced the same problem in our IIS app:
System.Security.Cryptography.Pkcs.PkcsUtils.CreateSignerEncodeInfo(CmsSigner signer, Boolean silent)
System.Security.Cryptography.Pkcs.SignedCms.Sign(CmsSigner signer, Boolean silent)
System.Security.Cryptography.Pkcs.SignedCms.ComputeSignature(CmsSigner signer, Boolean silent)
Regenerating certs as mentioned here didnt help.
I also noticed that test console app works fine under pool user.
Problem disappeared after clearing "Enable 32-bit Applications" setting for IIS app pool.
I tried everything in this post, but for me the solution was:
Import the original .p12 file into my local machine
Export it as a .pfx file checking the "Export all extended properties" and "Include all the certificates in the certification path if its possible" options
Read the new certificate using this flag options from this post solution:
var certificate = new X509Certificate2(certKeyFilePath, passCode,
X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet );
For a .net 4.8 WCF service I was getting this error because i hadn't (also) set the httpRuntime version.
Without the httpRuntime set i was getting the error whenever a secured .net tcp binding was present.
<system.web>
<compilation targetFramework="4.8" />
<httpRuntime targetFramework="4.8" />
<!--<customErrors mode="Off" />-->
</system.web>
I had already setup the required permissions in mmc.exe -> add snap-in -> certificates -> personal -> all tasks -> manage private keys -> add [computer name]\IIS_IUSRS (all my application pools are ApplicationPoolIdentity [default] - which is covered by IIS_IUSRS)
the PrivateKey and PublicKey properties are outdated beginning with NET 4.6. Please use:
cert.GetRSAPrivateKey() or GetECDsaPrivateKey()
cert.GetRSAPublicKey() or GetECDsaPublicKey()
Regards
After following the accepted answer (specifying KeySpec), the exception changed to System.Security.Cryptography.CryptographicException: Invalid provider type specified.. I resolved that exception by giving my web application access to the private key (IIS_IUSRS). I discovered that this would also fix the problem with my original certificate. So before generating and deploying new certificates, check the private key's permissions as well.
The problem is your code is not able to read the PFX file.
convert the pfx file to RSA format by doing the below steps.
Get the certificate and extract the pfx file from the certificate.
use the password 123456 below to have a quick solution.
rename your pfx to 'my.pfx' file to make it simple and put it in "C:\Certi"
make sure you have open SSL installed already in the system.
open cmd in windows system and type --> OpenSSL
Stay cool and just run these ones by one --> copy paste.
*Note
-passin is your Pfx file password
-passout is the new password for converted pfx.
1. pkcs12 -in "C:\Certi\my.pfx" -nokeys -out "C:\Certi\MYCERT.cer" -passin "pass:123456"
2. pkcs12 -in "C:\Certi\my.pfx" -nocerts –out “C:\Certi\MYCERT.pem" -passin "pass:123456" -passout "pass:123456"
3. rsa -inform PEM -in "C:\Certi\MYCERT.pem" -out "C:\Certi\MYCERT.rsa" -passin "pass:123456" -passout "pass:123456"
if you face issue in the 3rd command go here https://decoder.link/converter
Click PKC#12 To PEM
upload your pfx file and get it converted online.
download the zip file.
it contains 3 files. just copy ".key" file and rename it as my.key and put in "C:\Certi"
4. rsa -in C:\Certi\my.key -out C:\Certi\domain-rsa.key
5. pkcs12 -export -in "C:\Certi\MYCERT.cer" -inkey "C:\Certi\domain-rsa.key" -out "C:\Certi\CONVERTED.pfx" -passin "pass:123456" -passout "pass:123456"
**Also, you can try below things if the issue still persists**
Given access to app pool or IIS user to folder "mandatory to do"
Path --- > C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys
Delete the old keys (clear up the mess)
Path --- > C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys
This error went away after i removed the certificate from the store and imported it again from the .pfx file, using the Certificate Import Wizard (double-click the .pfx file) and an extra import option.
After checking the import option (same step where password is entered):
"Mark this key as exportable. This will allow you to back up or transport your keys at a later time."
The privatekey could now be accessed from code without any errors.
I also explicitly select the "Personal" store on the 2nd to last step, but I don't think this matters.
It seems on Windows 10, if I run my program without administrative privileges (whether I use the PrivateKey property or the GetRSAPrivateKey() extension method), I will see this exception (see the title of this discussion thread for the exception). If I run my program with administrative privileges and use the PrivateKey property, I will also see this exception. Only if I run my program with administrative privileges and use the GetRSAPrivateKey() extension method, I will not see this exception.
I also had this issue, I changed the '.jks' to '.pfx' without typing the password, once I changed the key store type again with the password it is worked successfully.
After experiencing this issue on some computers in a security-critical environment, I found that, contrary to several answers, updating the Cryptographic Service Provider (CSP) of the certificate from the old CAPI standard to the new CNG standard resolved this issue.
The security policies probably restricted the use of CSPs relying on deprecated cryptographic algorithms.
Updating the CSP to Microsoft Software Key Storage Provider based on these instructions fixed the issues.
I had a very strange issue regarding an imported PFX from OpenSSL on a Linux System and the ADWS (Active Directory Web Service).
When using the Import Certificate GUI through the MMC Snap Ins, the certificate was imported successfully and ADWS could find it and load.
When using this command with Powershell, the certificate appeared to have been imported successfully but ADWS was giving an Event ID 1402 error.
Import-PfxCertificate -FilePath C:\Users\Administrator\Desktop\dc01.test.pfx `
-CertStoreLocation 'Cert:\LocalMachine\My' -Password (ConvertTo-SecureString 'Password123!' -AsPlainText -Force)
Active Directory Web Services was unable to process the server certificate. A certificate is required to use SSL/TLS connections. To use SSL/TLS connections, verify that a valid server authentication certificate from a trusted Certificate Authority (CA) is installed on the machine.
Enabling the debugging this was the culprit:
ADWSHostFactory: [04/01/2023 21:55:59] [6] ProvisionCertificate: caught a CryptographicException: System.Security.Cryptography.CryptographicException: Invalid provider type specified.
at System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)
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 Microsoft.ActiveDirectory.WebServices.ADWSHostFactory.ProvisionCertificate(ServiceHost host)
I was stumped and tried some of the other solutions here but nothing worked.
Fortunately I discovered this guide from Component Space (https://www.componentspace.com/Forums/1578/SHA256-and-Converting-the-Cryptographic-Service-Provider-Type) which mentions the Cryptographic Provider being the issue as do a lot of others on this issue.
I could not see a way of changing this with powershell, but you can do this with certutil.
certutil.exe -p 'Password123!' -csp "Microsoft Enhanced RSA and AES Cryptographic Provider" -importPFX C:\Users\Administrator\Desktop\dc01.test.pfx
The certificate now works as if I had added it via the GUI.
What I can't understand is why Powershell is adding it with a different cryptographic provider than if I add it through the MMC GUI. I hope this helps some one else with a really weird and annoying bug.

How to create a certificate to use with SslStream AuthenticateAsServer without importing

I'm lost in a twisty maze of certificates and private keys.
I am writing a server in C#. I want it to accept SSL connections. For test purposes, I want to generate a certificate for "localhost", and use that certificate for the server. Ideally I don't want to pollute my certificate store, so I just want a file, or files, on disk that the C# code can load to provide the certificate.
Everything I have tried (using cookbook approaches suggested by web searches) either gives me "The credentials supplied to the package were not recognized" or "The server mode SSL must use a certificate with the associated private key."
Is there a way to generate a certificate and private key, and subsequently to load them into an X509Certificate object without also loading them into my machine certificate store?
In the end, I ran the following to create a server.pfx file:
makecert.exe -r -pe -n "CN=localhost" -sky exchange -sv server.pvk server.cer
pvk2pfx -pvk server.pvk -spc server.cer -pfx server.pfx
Then I loaded it in code with:
certificate = new X509Certificate2("server.pfx", "password");
(I didn't actually hard code the password like that :-)
The trick was to know that I needed a pfx file, and that I needed to load it using the X509Certificate2 class, rather than X509Certificate.
Niki Loche method works.
If you get The specified network password is not correct., then you should try it without password in C#. It doesn't matter what your input password was in makecert.
certificate = new X509Certificate2("Server.pfx", "");
But if you want to use password (there is a reason, it's there :)), try changing pvk2pfx.exe command to:
pvk2pfx.exe" -pi password -pvk Server.pvk -spc Server.cer -pfx Server.pfx
and in C# enter:
certificate = new X509Certificate2("Server.pfx", "password");
Password must be the same as it is in creating cer file.
That did the trick for me. I hope it will help someone.
There are a number of tools that should let you act as your own CA and generate a certificate. XCA is one of them. There are also a number of methods using OpenSSL commands, for example.
Generating a self-signed certificate only may seem like the easiest option, but using a test CA (and a separate server certificate) may be worth it. This would allow you to import the test CA into the browser's store if needed to make the tests more realistic. It's not much more difficult with the right tools (e.g. XCA).
Once you have generated your server certificate and its private key, turn it into a PKCS#12 file (.p12/.pfx).
You should then be able to load it using X509Certificate2.import(...). (See example in this answer.)
I recently developed a SMTP server with TLS. I needed to install the server as a Windows Service on a Windows Server 2012 box. I used a Let's Encrypt SSL Certificate (* wildcard) for my domain. While developing "on the server" I had to run Visual Studio as Administrator for my code to work using "new X509Certificate("cert.pfx", "{password}") and it ran perfectly. However, once installed as a service, that scheme did not work. Turns out the simpler/safer way is to use X509Store. Here's the code that solved the problem...
private X509Certificate GetSslCertificate()
{
X509Certificate cert = null;
string certname = "*.mydomain.com";
try
{
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.OpenExistingOnly);
foreach (X509Certificate certificate in store.Certificates)
{
if (certificate.Subject.ToLower().Contains(certname))
{
cert = certificate;
break;
}
}
}
catch (Exception) { /* Handle Exception */ }
return cert;
}
Also notice I used "LocalMachine" as the StoreLocation. You'll need to change to CurrentUser if that's where you installed your SSL Certificate.
If you have IIS, you can do it through the IIS Manager as detailed here:
http://blogs.msdn.com/b/ericnel/archive/2009/09/22/using-iis-to-generate-a-x509-certificate-for-use-with-the-windows-azure-service-management-api-step-by-step.aspx
I had the same issue when importing a machine certificate from the machine store (not the user store). I solved it by running Visual Studio as an administrator, as administrator privileges are required to access the private key of a machine store certificate.

Categories