How to chain SSL certificates in Perl? - perl

Perl newbie here.
I need to chain an intermediate CA x509 certificate to my client certificate.
Net::SSLeay::set_cert_and_key($ctx, $crt, $key);
my $bio = Net::SSLeay::BIO_new_file("subca.crt", 'r');
my $x509 = Net::SSLeay::PEM_read_bio_X509($bio);
Net::SSLeay::CTX_add_extra_chain_cert($ctx, $x509)
and die_if_ssl_error("CTX_add_extra_chain_cert"); # It dies here.
The certificate is in pem format. Can anyone help?
Edit: I have found that the call to "Net::SSLeay::PEM_read_bio_X509()" returns 0, which is an error condition.

This does exactly what I was looking for:
Net::SSLeay::CTX_use_RSAPrivateKey_file($ctx, $key, &Net::SSLeay::FILETYPE_PEM)
and die_if_ssl_error("private key");
Net::SSLeay::CTX_use_certificate_chain_file($ctx, $crt)
and die_if_ssl_error("CTX_use_certificate_chain_file");
$crt contains 2 certificates, first the client certificate, followed by the intermediate one.

Related

no data found in PEM block

Hashi Vault: Attempting to set a PEM-encoded certificate and private key bundle, using the pki/config/ca endpoint. The bundle.pem is a concatenation of the ca and private key. The following is the command and output
vault write pki2/config/ca pem_bundle=#bundle.pem
What is the proper format for the pem_bundle?
Resolution attempted
1. Removed all blank lines in the bundle.pem
2. Also tried to convert pem files to a string that can be passed in json
awk 'NF {sub(/\r/, ""); printf "%s\n",$0;}' cert-name.pem
3. Also tried the UI as well as the api interface.
4. Reviewed similar items on github regarding 'no data found in PEM block'; did not resolve issue.
vault write pki2/config/ca pem_bundle=#bundle.pem
I expect the output to be:
Success! Data written to: pki/config/ca
The actual results are
PUT http://127.0.0.1:8200/v1/pki2/config/ca
Code: 400. Errors: * no data found in PEM block
After further research, there is an issue with the private key formatting.
The private key needs to be changed from pkcs8 to pkcs1
openssl rsa -in pkcs8.key -out pkcs1.key -outform pem
Then recreate bundle using the pkcs1 formatted private key.
Then the following command is successful.
vault write pki2/config/ca pem_bundle=#bundle.pem

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

Can SSL cert be used to digitally sign files?

I want to ask a thing about digital signing I am not very sure.
Instead of creating a self signed certificate to use to sign some (PDF) files, I wanted to take my SSL cert which have my data already verified.
But the question is: Can a SSL cert be used to digital sign files or is it incompatible in some manner?
EDIT: To clarify, this question is not about how to sign PDFs, is only about if a SSL cert can be used (or converted in any way) to sign files.
To support digital signing certificate must have digitalSignature option in it's keyUsage field (and codeSigning option in it's extendedKeyUsage field if your want to sign programs with it).
Signing may be done with existing tools or manually (java example, you are not asking for it, but this code snippet might be useful anyway):
byte[] bytesToSign = loadMyData();
KeyStore ks = KeyStore.getInstance("pkcs12", "SunJSSE");
ks.load(new FileInputStream("cert.p12"), "passwd1".toCharArray());
PrivateKey privateKey = (PrivateKey) ks.getKey("myalias", "passwd2".toCharArray());
Signature sig = Signature.getInstance("SHA1withRSA", ks.getProvider());
sig.initSign(privateKey);
sig.update(bytesToSign);
byte[] signature = sig.sign();
To make your own not self-signed certificate with openssl see this SO answer.
Also curious about signing PDF's - aren't separate hash sums of these files enough in your case?
edit: if you want any sign, not exactly X.509 sign by existing tools, you can extract RSA key from your cert and do signing without bothering about keyUsage field.
At the core, the certificate is just a normal RSA public key that's been signed by several authorities.
So yes, definitely possible.
Though I don't know of any easy-to-use widespread tools for the end-user for this.
Yes, you can sign and verify the signature of files using SSL certificates
Here is an example:
SSLCERT='/XXXX/ssl/certs/fqdn.pem'
SSLKEY='/XXXX/ssl/private_keys/fqdn.pem'
# You might not need to specify a CA
CACERTFILE='/XXXX/ssl/certs/ca.pem'
# File to sign
FILE='YYYYYYY'
# Signs, needs ${SSLKEY} and ${FILE}
openssl dgst -sha512 -sign ${SSLKEY} -out ${FILE}.sha512 ${FILE}
# Then transfer the following files to another server:
# - ${CACERTFILE}
# - ${SSLCERT}
# - ${FILE}
# - ${FILE}.sha512
# Check the certificate is valid
openssl verify -verbose -CAfile ${CACERTFILE} ${SSLCERT}
# Extract the pub key from the cert
openssl x509 -in ${SSLCERT} -pubkey -noout > ${SSLCERT}.pub
# Check the signature
openssl dgst -sha512 -verify ${SSLCERT}.pub -signature ${FILE}.sha512 ${FILE}

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

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