Generating private key in a file using certreq in powershell - powershell

I am using a powershell script to create Certificate Signing Request (CSR) using certreq. I need the private key in a file but the script is not generating that. I tried looking the documentation of certreq and other resources but found nothing. In INF setting I am setting Exportable = TRUE. here is the setting
$settingsInf = "
[Version]
Signature=`"`$Windows NT`$
[NewRequest]
KeyLength = 2048
Exportable = TRUE
MachineKeySet = TRUE
SMIME = FALSE
RequestType = PKCS10
ProviderName = `"Microsoft RSA SChannel Cryptographic Provider`"
ProviderType = 12
HashAlgorithm = sha256
;Variables
Subject = `"CN={{CN}},CN={{CN2}},O={{O}},DC={{DC}},DC={{DC2}}`"
[Extensions]
{{SAN}}
Another solution I tried is to use openssl to get private key and CSR. In this solution I am getting both private key and CSR but when I submit the CSR to CA then it throws following error
"message" : "Invalid Subject DN. The requested Subject DN is not compatible with the issuing CA.",
I am using openssl as follows
$subject = "`"/CN=$cn/CN=$cn2/O=$o/DC=$dc/DC=$dc2'"
openssl req -new -key $privateKeyPath -rand $randPath -subj $subject -out $csrPath
The Certificate Authority DN is as follows
"issuer_dn" : "CN=usa,O=SE,DC=abc,DC=com",
any suggestion to either get private key using certreq or why CA is throwing error when using openssl. Thanks

I found the solution. may be it will help someone else.
It appears that some CA require subject in a particular order which is not documented (Super Annoying).
The CA I was connected to require $subject in following way
$subject = "`"/DC=$dc2/DC=$dc/O=$o/CN=$cn2/CN=$cn'"
using subject like this in generating CSR is accepted by CA.

Related

How to hash256 and digitally sign (with a private key) a string in Powershell

Using openssl I generated a private key (let's say httpCert.key) and a self signed certificate. Now, I have a string which I want to hash256, digitally sign with my private key i.e. httpCert.key and do a base64 encoding.
I can do this all in bash using openssl as below:
signature=`printf "test" | openssl dgst -sha256 -sign httpCert.key | openssl base64 -A`
But struggling to find a way to do this in Powershell.
Based on a help from other post, I can do a simple hash and encode as below:
$hasher=[System.Security.Cryptography.HashAlgorithm]::Create('sha256')
$signSHA=$hasher.ComputeHash([System.Text.Encoding]::UTF8.GetBytes("test"))
$signature=[Convert]::ToBase64String($signSHA)
But not sure how to sign the hash256 with a private key. I tried using "System.Security.Cryptography.RSACryptoServiceProvider" but couldn't get it working.
$Certificate = [Security.Cryptography.X509Certificates.X509Certificate2]::new("g:\Drive\ff.pfx","")
$data=[System.Text.Encoding]::UTF8.GetBytes("test")
$sData=$Certificate.PrivateKey.SignData($Data,'SHA1')
[Convert]::ToBase64String($sData)

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

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())

Signing a certificate with my CA

On running:
openssl ca -in ${ALIAS}.csr -out user-cert.pem -keyfile cacert-private.pem -cert cacert.pem -passin pass:$PASSWD -config ${CONFIG}
I get:
The stateOrProvinceName field needed to be the same in the
CA certificate (Gloucestershire) and the request (Gloucestershire)
I've read the error a few times and I'm fairly sure the field is the same value in each case. I have found references to similar problems being caused by different encodings but I don't know how I should be specifying that and where.
This is the ${ALIAS}.csr:
-----BEGIN NEW CERTIFICATE REQUEST-----
MIICxzCCAa8CAQAwgYExCzAJBgNVBAYTAkdCMRgwFgYDVQQIEw9HbG91Y2VzdGVyc2hpcmUxEzAR
BgNVBAcTCkNoZWx0ZW5oYW0xHzAdBgNVBAoTFldhbnNkeWtlIEhvdXNlIExpbWl0ZWQxDjAMBgNV
BAsTBUZpemlvMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDWvivt1JHiuaNeadOQJtxynQ4sSAR/peWgKd8g9UQgNM+H9QW4NlRE81+g63BdRqZT5YMm
J4K3upovQNlDRklevslgEYoTdQM4yBKV676Q4XDbM7Vk+rt04sqL5IgdsAUXODfMJvu81t3tOjFc
OGO7S+B+LEJ1+8qshLbuK2gBigfgcZtlbNgW6fCGik8ZsrKWl8W+NFbw1seS01INAipwCBasxaaj
/lINwWQVbQIG09+vEdwuHmmq5VIKlJqFcYNUTFBVojoJLfzyStZR2PfFUxp7R+t2YmVj6a48B7NA
lODnIlQDkAprECNMpCZoSP1QjrZgW1BgaVbT5OaWlVsPAgMBAAGgADANBgkqhkiG9w0BAQUFAAOC
AQEAvalFyJOgzmd1jcFlS5YoqiNgX1bm9nZ0/cFgj6cGL7R0Gqc9wu5QPakWRxa9c2UcI0m7p1lp
cygDvQTY23LEBhVcruymIGQG5DhDpXHeaBCbV3OWO6xowAjh+riQjvTNeVSXtP3jUNs5DaId0z+A
GXeb7dR96jhyj+soNYENoQseQLqLdAW4p0jdK1BraMJTc0ber0FBx1nOUXOEoTIJL9kL9cUWaCp3
7uYkonIPtVCCfS8KcgXxUsNMC41q/SkKDVB23PeCjnWgcyXxnSpx8n+AK7fwMgh+4TcZ5usmVujR
MNqk84hZpw8h1FIcmqRaWtaPWyv3EX8JH5LTnDe3eQ==
-----END NEW CERTIFICATE REQUEST-----
And cacert.pem:
-----BEGIN CERTIFICATE-----
MIIDQDCCAqmgAwIBAgIJAPj9mvMDl1K/MA0GCSqGSIb3DQEBBQUAMIG4MQswCQYD
VQQGEwJHQjEYMBYGA1UECAwPR2xvdWNlc3RlcnNoaXJlMRMwEQYDVQQHDApDaGVs
dGVuaGFtMR8wHQYDVQQKDBZXYW5zZHlrZSBIb3VzZSBMaW1pdGVkMQ4wDAYDVQQL
DAVGaXppbzESMBAGA1UEAwwJbG9jYWxob3N0MTUwMwYJKoZIhvcNAQkBFiZyaWNo
YXJkLm1pZHdpbnRlckB3YW5zZHlrZS1ob3VzZS5jby51azAeFw0xMTA4MDcyMTU4
NDBaFw0yMTA4MDQyMTU4NDBaMIG4MQswCQYDVQQGEwJHQjEYMBYGA1UECAwPR2xv
dWNlc3RlcnNoaXJlMRMwEQYDVQQHDApDaGVsdGVuaGFtMR8wHQYDVQQKDBZXYW5z
ZHlrZSBIb3VzZSBMaW1pdGVkMQ4wDAYDVQQLDAVGaXppbzESMBAGA1UEAwwJbG9j
YWxob3N0MTUwMwYJKoZIhvcNAQkBFiZyaWNoYXJkLm1pZHdpbnRlckB3YW5zZHlr
ZS1ob3VzZS5jby51azCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA03Y4hYdd
at3e3AB98s+E5wlxrvRL8RhJtRac0Jt0gXQy12ZYziFm3gryx0IG02srXluM+V3/
BPRRCLsnEnltfoi/fE0wM9MT0V1Ao9EXQ5t1E2rOzdoXUUdvovd6qvwG2L/DHCdL
kKjhokVR9TkFW/AWctBdWkb9qfFFTpDY4i0CAwEAAaNQME4wHQYDVR0OBBYEFHbG
d3+Lzax90slk65y1BYDgZ897MB8GA1UdIwQYMBaAFHbGd3+Lzax90slk65y1BYDg
Z897MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEArZ2yfTGJK3R+jRwP
FjaonDy1NVOt9tgjHfyh9YNQfyFSC7R987wFPcyydEqh8xg/Lb3WGwseDuzCBusw
jmVIqiUYBClHzkF3jG1766ltdlVVTOavVQgQMRBGMvpHVxcMH2RUNUyWH0XW+DH2
/uuRRpu4vX5sfEW75uEfORB9Mrg=
-----END CERTIFICATE-----
Any ideas? Thanks in advance.
You can also set the attributes as optional:
# For the CA policy
[policy_match]
countryName= optional
stateOrProvinceName= optional
organizationName= optional
organizationalUnitName= optional
commonName= supplied
emailAddress= optional
I have also run into this problem. Thanks to the replies above (mainly Francois), I discovered the source of the problem.
openssl is encoding using UTF8STRING and keytool (Java 6) is encoding with PRINTABLESTRING.
Worked around it by changing the openssl configuration so it matches keytool. In /usr/ssl/openssl.cnf change the "string_mask" setting to "pkix".
The previous posters already answered the question, but to make it easier, here is an example how to specify the encoding. Use the string_mask:
[ req ]
default_bits = 2048
default_md = rsa
prompt = no
string_mask = utf8only # <--------------
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
countryName = GB
stateOrProvinceName = Gloucestershire
localityName = Cheltenham
organizationName = Wansdyke House Limited
organizationalUnitName = Fizio
commonName = localhost
As shown by :
openssl asn1parse -in req.csr
the request DN strings are encoded as PRINTABLESTRING.
openssl asn1parse -in cacert.pem
shows the CA DN strings are encoded as UTF8STRING.
For a quick'n dirty hack, I suggest you change the encoding of strings in your request by replacing the encoding type for PRINTABLESTRING (0x13) by the type for UTF8STRING (0x0c), using your favorite hex editor.
You will have to convert your request in DER before poking it.
The offset of bytes to change can be found with :
openssl asn1parse -in csr |grep PRINTABLESTRING |awk -F":" '{print $1}'
Then try to sign again.
I just ran into this problem. The root cause is a mismatch between the values of string_mask in the client's and the CA's openssl.cnf. The easy fix is to modify the client's value to match what the CA expects, then regenerate the CSR. The hard fix is to edit the CA's value and start a fresh CA.
Promoting mbrownnyc's comment to an answer, as it was useful to me and deserves more attention.
I believe /usr/ssl/openssl.cnf contains a policy called policy_anything that contains the above setup. You can use it by utilizing the policy argument as follows:
openssl ca -policy policy_anything -days 365 -out /root/ca/certs/out.pem -in certreq.csr

add or create 'Subject Alternative Name' field to self-signed certificate using makecert

How can I create a certificate using makecert with a 'Subject Alternative Name' field ?
You can add some fields eg, 'Enhanced Key Usage' with the -eku option and I've tried the -san option but makecert doesn't like it.
This is a self-signed certificate so any method that uses IIS to create something to send off to a CA won't be appropriate.
An even easier way is to use the New-SelfSignedCertificate PowerShell commandlet, which includes a SAN by default. In a single command you can create the certificate and add it to the store.
New-SelfSignedCertificate -DnsName localhost -CertStoreLocation cert:\LocalMachine\My
Note that you need to run PowerShell as an administrator.
Makecert doesn't appear to support SANs so I created a certificate with SANs for use with IIS using OpenSSL. Check out my blog post about it:
IIS 7 provides some easy to use wizards to create SSL certificates,
however not very powerful ones. What I needed to do was to create SSL
certificates that included a x.509 V3 extension, namely subject
alternative names, a.k.a SANs. What SANs do is allow the website
certificate to validate incoming requests by more than one URL domain
name. This is really important when the web server is running web
services such as WCF services and when other web services connect to
them over SSL connections as with service oriented architectures.
Unless special code is added to the web services to override the
default SSL validation handler routines, the common name (CN) of the
certificate MUST match the incoming request URL domain. So if the
request was made using an FQDN, the certificate must have the FQDN as
a CN or a SAN, a IP address or just a hostname will cause an SSL
validation error and the connection will fail.
SANs to the rescue… SANs support, among other things, DNS names and IP
addresses. So by creating the certificate with SANs of the server FQDN
and IP address, it increases the ways that other web services can
connect.
There are a number of tools that can generate certificates:
makecert.exe, keytool.exe (java), selfssl.exe and openssl.exe. In
addition, starting with Windows Vista and Server 2008 Microsoft added
the CertEnroll API which can also create certificates programmatically
either through COM interfaces.
OpenSSL ended up doing exactly what I needed it to do. The process was
fairly straight forward.
Construct an OpenSSL config file.
[req] distinguished_name = req_distinguished_name x509_extensions =
v3_req prompt = no [req_distinguished_name] C = US ST = VA L =
Somewhere O = MyOrg OU = MyOU CN = MyServerName [v3_req] keyUsage =
keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth
subjectAltName = #alt_names [alt_names] DNS.1 = MyServerName DNS.2 =
10.0.1.34 IP.1 = 10.0.1.34 IP.2 = 192.167.20.1
Create x509 request with OpenSSL
openssl.exe req -x509 -nodes -days 730 -newkey rsa:2048 -keyout
C:\cert.pem -out C:\cert.pem -config C:\PathToConfigFileAbove.txt
Create a PFX containing the keypair
openssl.exe pkcs12 -export -out C:\cert.pfx -in C:\cert.pem -name "My
Cert" -passout pass:mypassword
Import the PFX into IIS using the import link in the server
certificates area.
Bind the certificate to the IIS websites.
And viola, we know have a SSL certificate for IIS with SANs so we can
connect using multiple domain names without certificate validation
errors.
Source: Creating certificates with SANs using OpenSSL by Andy Arismeti, Thursday, September 1, 2011
Update
The certificate generated using the below makecert method does not work reliably in all browsers, because it does not actually generate a "Subject Alternative Name".
If you examine the certificate you will see that it does not actually have a Subject Alternative Name field, but instead specifies multiple CN in the Subject field.
E.g.
Subject:
CN = blah.foo.corp
CN = blah
Whereas a real "SAN" cert would have something like:
Subject Alternative Name:
DNS Name=blah.foo.corp
DNS Name=blah
To understand the differences and history between the "Subject" field with "Common Name" and the "Subject Alternative Name" field, I recommend reading The (soon to be) not-so Common Name.
So it appears that makecert cannot be used to generate a true "SAN" cert, and you will need to use other tools, such as openssl.
Original Answer:
At least with the version of makecert that comes with Visual Studio 2012, you can specify multiple subjects, simply by specifying a comma separated list -n "CN=domain1, CN=domain2"
E.g. (from the technet blog Makecert.exe SAN and Wildcard certificate)
makecert -r -pe -n "CN=*.fabrikam.com, CN=*.contoso.com" -b 01/01/2010 -e 01/01/2100 -eku 1.3.6.1.5.5.7.3.1,1.3.6.1.5.5.7.3.2,1.3.6.1.5.5.7.3.3,1.3.6.1.5.5.7.3.4 -ss my -sr localMachine -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 -len 2048

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