Create certificate and CSR using Windows PowerShell/.Net without using external programs or libraries - powershell

I would like to create a Private Key and a CSR, submit the CSR to a Certificate Authority, retrieve the certificate once issued, and have the Private Key and Certificate as separate PEM files suitable for use in non-Microsoft applications (they are generally web servers). I'd like to avoid using Java Keytool or OpenSSL to generate keys and certificate signing requests in Windows PowerShell on Windows Server 2016. The CSRs will be submitted to a Microsoft Active Directory Certificate Services.
OpenSSL and Java are not (and won't be) installed on the computers requiring certificates. As the certificates are for non-Microsoft applications, I also want to avoid using the Certificate Store on the computers. I don't mind using "certreq" to actually submit the completed CSR and retrieve the resulting certificate once approved.
I have some code, based on C# Export Private/Public RSA key from RSACryptoServiceProvider to PEM string, which will extract the private key from an X509Certificate2. So far, as an experiment, I have used this successfully with a PKCS12 keystore (where the key and CSR were created with Keytool).
Inspired by Automate the process of creating a private key, a CSR and a final Signed Certificate in .NET Core I knocked together the following, but ran out of inspiration, and didn't really know what I was doing. How do I complete the process of submitting the CSR to the CA (or outputting the CSR as a file for using with certreq)?
[int]$KeyLength = 2048
$ComputerName = "jon"
$Domain = "domain.local"
[string]$DistinguishedName = "CN=$($ComputerName).$($Domain),OU=Unit,O=Org,C=GB"
$HashAlgo = [System.Security.Cryptography.HashAlgorithmName]::SHA256
$RSASigPadding = [System.Security.Cryptography.RSASignaturePadding]::Pkcs1
$RSAKey = [System.Security.Cryptography.RSA]::Create($KeyLength)
$Certificate = [System.Security.Cryptography.X509Certificates.CertificateRequest]::new($DistinguishedName,$RSAKey,$HashAlgo,$RSASigPadding)
# Add Basic Constraints
$BasicConstraints = [System.Security.Cryptography.X509Certificates.X509BasicConstraintsExtension]::new($false,$false,0,$false)
$BCExtension = [System.Security.Cryptography.X509Certificates.X509Extension]::new($BasicConstraints,$false)
$Certificate.CertificateExtensions.Add($BCExtension)
# Add Subject Key Identifier extension
$SubjectKeyIdentifier = [System.Security.Cryptography.X509Certificates.X509SubjectKeyIdentifierExtension]::new($Certificate.PublicKey,$false)
$SKIExtension = [System.Security.Cryptography.X509Certificates.X509Extension]::new($SubjectKeyIdentifier,$false)
$Certificate.CertificateExtensions.Add($SKIExtension)
# Add Key Usage
$KeyUsageFlags = [System.Security.Cryptography.X509Certificates.X509KeyUsageFlags]::DigitalSignature -bor [System.Security.Cryptography.X509Certificates.X509KeyUsageFlags]::KeyEncipherment
$KeyUsage = [System.Security.Cryptography.X509Certificates.X509KeyUsageExtension]::new($KeyUsageFlags,$true)
$KUExtension = [System.Security.Cryptography.X509Certificates.X509Extension]::new($KeyUsage,$true)
$Certificate.CertificateExtensions.Add($KUExtension)
# Add EKU
$ServerAuthentication = [System.Security.Cryptography.Oid]::New("Server Authentication")
$EKUOidCollection = [System.Security.Cryptography.OidCollection]::new()
$EKUOidCollection.Add($ServerAuthentication) | out-null # this outputs 0
$EnhancedKeyUsage = [System.Security.Cryptography.X509Certificates.X509EnhancedKeyUsageExtension]::new($EKUOidCollection,$false)
$EKUExtension = [System.Security.Cryptography.X509Certificates.X509Extension]::new($EnhancedKeyUsage,$false)
$Certificate.CertificateExtensions.Add($EKUExtension)
# Add SAN
$SubjectAlternateNameBuilder = [System.Security.Cryptography.X509Certificates.SubjectAlternativeNameBuilder]::new()
$SubjectAlternateNameBuilder.AddDnsName("$($ComputerName).$($Domain)")
$Certificate.CertificateExtensions.Add($SubjectAlternateNameBuilder.Build())

Related

Using PowerShell created self signed certs

I am trying to create a new self signed cert and then use it to sign an EXE, to address the fact that idiot Autodesk is using an installer that ignores the time stamp and refuses to install something with an expired cert, even their own installer. It's a bug, they know it, and they fixed their installer. But addressing that when you have 30+ deployments that use the old buggy one is a PITA. Anyway...
I expected this would produce a certificate that I would find in the Personal tab.
$certParameters = #{
'DnsName' = 'PxTools'
'Type' = 'CodeSigningCert'
'CertStoreLocation' = 'Cert:\LocalMachine\My'
'NotAfter' = (Get-Date).AddMonths(12)
'KeyAlgorithm' = 'RSA'
'KeyLength' = '4096'
}
$cert = New-SelfSignedCertificate #certParameters
and that I would then be able to use it for signing. But it doesn't populate the $cert variable as expected, and it puts the cert in Intermediate Certification Authorities, and I have yet to figure out how to use
Get-ChildItem cert:\LocalMachine\?? -codesign
To get certs in that location. I have used ??=My to get a cert in Personal, but not sure
A: Why the cert isn't created in Personal as expected
B: How to get the cert from where it IS created

Is there a way to generate self signed certificates (X509 v3 with Subject Alternative Name) with my own root certificate to replace MakeCert.exe

I am having issues with makecert not able to generate a self-signed SSL certificate with Subject Alternative Name (SAN) in place. Latest versions of Google Chrome gives a security error when the website is accessed over HTTPS. I have read through several articles to try and understand the context and have come to the conclusion that makecert is old enough and won't be able to support X509 v3 certificate generation with SAN. Is there an alternative means for generating a self signed root certificate and intermediate certificates based on that root CA using something else that can run in Windows 7 and above please?
Root certificate is generated as follows:
makecert.exe -pe -ss Root -sr LocalMachine -n "CN=DIGITALMARKETRESEARCHAPPS PTY LTD, O=DIGITALMARKETRESEARCHAPPS PTY LTD, OU=DIGITALMARKETRESEARCHAPPS PTY LTD" -eku 1.3.6.1.5.5.7.3.1 -r -cy authority -a sha256
Intermediate certificate with the above Root CA is created as follows:
makecert.exe -pe -ss my -n "CN=www.myawesomesite.com.au, O=DIGITALMARKETRESEARCHAPPS PTY LTD, OU=DIGITALMARKETRESEARCHAPPS PTY LTD" -sky exchange -in "DIGITALMARKETRESEARCHAPPS PTY LTD"
I cannot seem to find a way to use either New-SelfsignedCertificateEx or New-SelfSignedCertificate to map exactly to the parameter above and create a certificate with the given root CA.
I will be really thankful for any help in the correct direction please.
At the moment, there is this old application that our clients use which makes use of makecert.exe to generate SSL certificates on the fly. Unfortunately this was done a long time ago and is hard to go back and tell them to change the whole architecture at this point. Google Xhrome in particular has been complaining about these certificates generated by makecert as explained in this article below:
http://news.thewindowsclub.com/deprecation-coming-to-google-chrome-heres-how-it-could-affect-your-workflow-88723/
http://www.telerik.com/blogs/understanding-fiddler-certificate-generators
The upcoming release of .NET Core 2.0 has added new classes to help here. While I know that either "powershell can" or "there's a version of powershell that does" work with .NET Core I don't know how, so there may be an adapter required for this answer.
Given signingCert, an X509Certificate2 instance which HasPrivateKey==true:
private static X509Certificate2 CreateNewCertificate(
X509Certificate2 signingCert,
int newRsaKeySize,
IEnumerable<string> sanDnsEntries)
{
var sanBuilder = new SubjectAlternativeNameBuilder();
string primaryDnsName = null;
foreach (string dnsEntry in sanDnsEntries)
{
// Let's just use the first one as the subject.
primaryDnsName = primaryDnsName ?? dnsEntry;
sanBuilder.AddDnsName(dnsEntry);
}
// New .NET Core Create(int) method. Or use
// rsa = RSA.Create(), rsa.KeySize = newRsaKeySize,
// or (on .NET Framework) new RSACng(newRsaKeySize)
using (RSA rsa = RSA.Create(newRsaKeySize))
{
var certRequest = new CertificateRequest(
$"CN={primaryDnsName}, O=Et OU=Cetera",
rsa,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
// Explicitly not a CA.
certRequest.CertificateExtensions.Add(
new X509BasicConstraintsExtension(false, false, 0, false));
certRequest.CertificateExtensions.Add(
new X509KeyUsageExtension(
X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment,
true));
// TLS Server EKU
certRequest.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(
new OidCollection
{
new Oid("1.3.6.1.5.5.7.3.1"),
},
false));
// Add the SubjectAlternativeName extension
certRequest.CertificateExtensions.Add(sanBuilder.Build());
// Serial number.
// It needs to be unique per issuer.
// CA/Browser forum rules say 64 or more bits must come from a CSPRNG.
// RFC 3280 says not to use more than 20 bytes.
// Let's use 16 (two C# `long`s)
byte[] serialNumber = new byte[16];
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
{
rng.GetBytes(serialNumber);
}
// If you care about monotonicity (and believe your clock is monotonic enough):
{
long ticks = DateTime.UtcNow.Ticks;
byte[] tickBytes = BitConverter.GetBytes(ticks);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(tickBytes);
}
Buffer.BlockCopy(tickBytes, 0, serialNumber, 0, tickBytes.Length);
}
DateTimeOffset now = DateTimeOffset.UtcNow;
return certRequest.Create(
signingCert,
now,
now.AddDays(90),
serialNumber);
}
}
API Documentation

How would I generate the Identity Server signing certificate

In the identity server samples we find code like this in Startup.cs
var certFile = env.ApplicationBasePath + "\\idsrv3test.pfx";
var signingCertificate = new X509Certificate2(certFile, "idsrv3test");
How would I go about replacing this for production scenarios?
For the record, the code proposed in the image posted by RuSs:
options.SigningCertificate = LoadCertificate();
public X509Certificate2 LoadCertificate()
{
string thumbPrint = "104A19DB7AEA7B438F553461D8155C65BBD6E2C0";
// Starting with the .NET Framework 4.6, X509Store implements IDisposable.
// On older .NET, store.Close should be called.
using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadOnly);
var certCollection = store.Certificates.Find(X509FindType.FindByThumbprint, thumbPrint, validOnly: false);
if (certCollection.Count == 0)
throw new Exception("No certificate found containing the specified thumbprint.");
return certCollection[0];
}
}
Get a dedicated cert - either via your PKI or self-generate one:
http://brockallen.com/2015/06/01/makecert-and-creating-ssl-or-signing-certificates/
Import the key pair into the Windows certificate store, and load it from there at runtime.
To step up security, some people deploy the keys to a dedicated device (called an HSM) or to a dedicated machine (e.g. behind a firewall). The ITokenSigningService allows moving the actual token signing to that separate machine.
Recently I decided to revamp my token signing issuing process. If you're running Windows 10, you can use the awesome powershell cmdlet called New-SelfSignedCertificate.
Here is my example usage:
New-SelfSignedCertificate -Type Custom
-Subject "CN=TokenSigningForIdServer"
-TextExtension #("2.5.29.37={text}1.3.6.1.5.5.7.3.3")
-KeyUsage DigitalSignature
-KeyAlgorithm RSA
-KeyLength 2048
-CertStoreLocation "Cert:\LocalMachine\My"
Make sure you are running the command as an admin. You can obtain the certificate details by opening certlm.msc. It should be stored below Personal\Certificates.
Most of the flags should be obvious, apart from the -TextExtention one. It specifies that an Enhaced Key Usage field is set to the "Code Signing" value. You can play around with the algorithm used, key length, even add extentisons by refering to the following documentation page.
Here is how I load it from a thumbprint in my config:
Click here to see image

Importing .pfx within BouncyCastle-Keystore

i've a problem concerning the import of a .pfx certificate into a bouncycastle-keystore. The error message says that "...tampered keystore file or incorrect PKCS12 Password...". I've exported the certificates with Windows' CertMgr.
The certificates are exported as .pfx files. I want to import the certificates with their private keys in order to use them in combination with tls' client authentication.
I would appreciate for any help.
Windows's PFX files are just renamed PKCS#12 files, and you don't even need BouncyCastle to import them: you can use Java's built-in KeyStore API (which has no limitations on password length or composition -- if you want "no password" you can use the empty string).
Usually, PKCS12 / PFX import code looks something like this:
FileInputStream fis = new FileInputStream("your.pfx");
String password = "your-password";
KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(fis, password.toCharArray());
String alias = ks.aliases().nextElement();
PrivateKey pKey = (PrivateKey)ks.getKey(alias, password.toCharArray());
X509Certificate cert = (X509Certificate)ks.getCertificate(alias);
Not sure about your case - but a lot of tools have implied assumptions about having a password on the private key and/or the same on the PKCS#12 enclosure; it being the same and being 4 or 6 chars. I found that using something like 'abcd1234' is a fairly safe one to use across vendors (or a real one of course).

Setting Key Usage attributes with Makecert

Is it possible to set Key Usage attributes using makecert, or any other tool I can use to generate my own test certificates?
The reason I'm interested is that certificates used for BizTalk Server AS2 transport require a key usage of Digital Signature for signing and Data Encipherment or Key Encipherment for encryption/decryption, and I want to play around with this feature.
I see how to set enhanced key usage attributes with makecert, but not key usage.
While you cannot make a self-signed cert and set the Enhanced Key Usage parameters using makecert I thought I'd save everyone the trouble of trying to use go down the path of using OpenSSL to generate a cert on Windows. Instead, you can use certreq (which is available if you already have makecert) and fashion your own request to set the required parameters.
For example, this sets up a cert with an EKU of Document Encryption (1.3.6.1.4.1.311.80.1) and key usages of Key Encipherment and Data Encipherment.
Create a new file, MyCert.inf:
[Version]
Signature = "$Windows NT$"
[Strings]
szOID_ENHANCED_KEY_USAGE = "2.5.29.37"
szOID_DOCUMENT_ENCRYPTION = "1.3.6.1.4.1.311.80.1"
[NewRequest]
Subject = "cn=me#example.com"
MachineKeySet = false
KeyLength = 2048
KeySpec = AT_KEYEXCHANGE
HashAlgorithm = Sha1
Exportable = true
RequestType = Cert
KeyUsage = "CERT_KEY_ENCIPHERMENT_KEY_USAGE | CERT_DATA_ENCIPHERMENT_KEY_USAGE"
ValidityPeriod = "Years"
ValidityPeriodUnits = "1000"
[Extensions]
%szOID_ENHANCED_KEY_USAGE% = "{text}%szOID_DOCUMENT_ENCRYPTION%"
Just set the Subject to whatever you need.
Then run:
certreq -new MyCert.inf MyCert.cer
This will generate the public key (X509 cert) and install it to your Current User personal store on the machine. You can export it from there if you want.
I used this to generate a certificate for encrypting PowerShell DSC, for testing.
For more details: https://technet.microsoft.com/en-us/library/dn296456.aspx#BKMK_New
Digital Signature,Data Encipherment and Key Encipherment can be add by using the PowerShell Cmdlet New-SelfSignedCertificate. One of the New-SelfSignedCertificate Parameters is KeyUsagewhere you can add DigitalSignature, DataEncipherment and KeyEncipherment.
New-SelfSignedCertificate is described on technet (https://technet.microsoft.com/library/hh848633)
Sample:
New-SelfSignedCertificate -Type Custom -Subject "CN=sample.com" -KeyUsage DataEncipherment, KeyEncipherment, DigitalSignature -TextExtension #("2.5.29.37={text}1.3.6.1.5.5.7.3.2,1.3.6.1.5.5.7.3.1") -CertStoreLocation "Cert:\CurrentUser\My"
The sample covers client authentication and server authentication and creates the certificate at the current user store under my.
MakeCert doesn't let you specify key usage, only extended key usage. I think you need a CA to create a broader certificate.
You can setup your own CA with ubuntu server. https://www.digitalocean.com/community/tutorials/how-to-create-a-ssl-certificate-on-apache-for-ubuntu-14-04
You can use the -eku option to specify the key usage to your certificate.
See details here:
http://msdn.microsoft.com/en-us/library/aa386968(VS.85).aspx