IdentityServer4: PFX certificate for production use? - c#

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.

Related

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" ...

Difference between MakeCert and OpenSSL wrt C# SslStream

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!

"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.

How to set the publisher name for a Windows Forms application

I have created the setup of a Windows Forms application. After installing this setup in Windows 7, it displays something like this:
Name: my application.exe
Publisher: unknown publisher
Type: application
From: my application.exe
I want to set the publisher name. How do I set the publisher name?
You need to digitally sign the output code. I can start you off with the article Signing and Checking Code with Authenticode.
The whole purpose of this is to guarantee your code has not been tampered with. If you purchase a code signing certificate from one of the certificate authorities, you can prevent the "do you trust this" window from appearing at all.
It's not a simple task to set up, but it can be performed with a script once it's up and going.
You won't find a simple, quick-fix answer.
Here's a cut and paste of the most relevant sections. You may need to read further to get exactly what you want.
MakeCert
Use the MakeCert test program to generate a test X.509 certificate. MakeCert performs the following tasks:
Creates a public/private key pair for digital signatures and associates it with a name that you choose.
Associates the key pair with a publisher's name that you choose.
Creates an X.509 certificate , signed by the test root key or one you specify, that binds your name to the public part of the key pair. The certificate is output to a file, a system certificate store, or both.
MakeCert Internet Explorer 3.02 UPD Example
The following is an example that creates a certificate using the Microsoft Internet Explorer 3.02 UPD options:
MakeCert -k:c:\KeyStore\MyKey.pvk -n:CN=MySoftwareCompany Cert.cer
In this example, a certificate file called Cert.cer is created. The public part of the key pair called MyKey is bound to the publisher, MySoftwareCompany.
Cert2SPC
After you have generated a certificate, you can create an software publishing certificate with the Cert2SPC program. This program wraps multiple X.509 certificates into a PKCS #7 signed-data object. Note that this program is for test purposes only. A valid software publishing certificate is obtained from a certificate authority.
Here is an example:
Cert2SPC MyCert.cer MyCert.spc
This wraps an X.509 certificate, MyCert.cer into a PKCS #7 software publishing certificate called MyCert.spc.
SignCode
The final step is to actually sign a file using the SignCode program. This program will:
Create a Cryptographic Digest of the file.
Sign the digest with your private key.
Copy the X.509 certificates from the software publishing certificate into
a new PKCS #7 signed-data object. The PKCS #7 object contains the serial
numbers and issuers of the certificates used to create the signature, the
certificates, and the signed digest information.
Embed the object into the file.
Optionally, it can add a time stamp to the file. A time stamp should always be added when signing a file. However, SignCode also has the ability to add a time stamp to a previously signed file subject to some restrictions (see the examples that follow the options table).
Once the file has been signed (assuming you have a valid certificate) and time stamped, the file can be distributed to your customers. Note that certificates generated with the test programs MakeCert and Cert2SPC are NOT valid for signing code that will be distributed to the public. Independent software vendors must obtain a certificate from GTE, VeriSign Inc., or another certification authority for signing code that will be distributed to the public.
SignCode Examples for Internet Explorer 3.02 UPD
Here are two examples of how to sign and time stamp a file using the Microsoft Internet Explorer 3.02 UPD options. The first uses a private-key name MyKey and the second uses a private-key file My.pvk:
SignCode -prog MyControl.exe -spc Cert.spc -pvk MyKey -timeStamper http://timestamp.verisign.com/scripts/timstamp.dll
SignCode -prog MyControl.exe -spc Cert.spc -pvk My.pvk -timeStamper http://timestamp.verisign.com/scripts/timstamp.dll
Note In the URL above, timstamp.dll is correct. This is not a typographical error.
In both cases a PKCS #7 object, Cert.spc, is embedded into the digest of the file, MyControl.exe. In the first example, the digest is signed with the private key of the MyKey key pair, and a time stamp is added. In the second example, the digest is signed with the private-key file My.pvk, and a time stamp is added.

Categories