Checking whether a certain root CA is trusted on the system - shell

My objective is to test whether a certain root certification authority is trusted by the active user. I currently have a working solution that I managed to piece together using several other answers1,2, but it seems very convoluted to me, so I'm asking for alternative (or at least, simplified) suggestions from people who (unlike me) know what they're doing.
I am assuming that this will be executed by a non-privileged user (i.e. one that cannot install new packages), so I would like to use utilities that are likely bundled with most unix/linux distros (unlike e.g. certutil). For this reason, the current solution uses awk, grep and openssl, which seem quite universal.
Another thing I should note is that I'm not concerned with the possible security implications that might arise from testing certificates the way I do.
Here's my current code:
awk -v cmd='openssl x509 -noout -issuer' '/BEGIN/{close(cmd)};{print | cmd}'
< /etc/ssl/certs/ca-certificates.crt
| grep -F 'issuer=C = US, O = company, CN = localhost, OU = engineering'
It uses awk in conjunction with openssl to iterate over all existing certificates, outputting their Issuer, then piping it to grep to test whether the required line exists.
The output I'm getting in the case of a positive match is the string I'm looking for, even though all I need is a binary answer (true/false, yes/no, 1/0, ...).
Any suggestion on how to achieve my goal in a simpler and/or more universal fashion?

You can spare the call to awk by processing all the certificates using openssl alone. According to this answer on Server Fault the following will use an intermediate conversion to provide the same amount of information (i.e. the issuer for each certificate in the input file) which can be filtered for the data you're looking for:
openssl crl2pkcs7 -nocrl -certfile /etc/ssl/certs/ca-certificates.crt \
| openssl pkcs7 -print_certs -noout \
| grep '^issuer=/C=US/O=company/CN=localhost/OU=engineering'
I find this an improvement because it doesn't use a bulky call to awk (which would also be another dependency), and the output of pkcs7 seems much more machine-readable than the whitespace-ridden original output from x509.
Note that you can use the return value of the above grep call to tell whether the given root CA is trusted:
openssl crl2pkcs7 -nocrl -certfile /etc/ssl/certs/ca-certificates.crt \
| openssl pkcs7 -print_certs -noout \
| grep -q '^issuer=/C=US/O=company/CN=localhost/OU=engineering' && echo 'Certificate found!'

Related

download cert with curl, echo cert value into openssl

I'm trying to get an intermediate certificate using curl, and process the certificate without writing it to a file.
I've figured out how to process a leaf cert and get the distribution point, but that's where I'm stuck.
For an example, let's take stackoverflow (seems pertinent). The first intermediate cert for SO is from http://cacerts.digicert.com/DigiCertSHA2HighAssuranceServerCA.crt.
In an interactive shell, I can do something like this to store the value of the certificate into a variable:
DigiCertIntermediateCert=$(curl http://cacerts.digicert.com/DigiCertSHA2HighAssuranceServerCA.crt)
Then I can check the value of the cert by doing something like this:
echo "$DigiCertIntermediateCert" | openssl x509 -inform DER -outform PEM
This outputs the nice and friendly PEM-formed cert. It looks like this:
-----BEGIN CERTIFICATE-----
MIIEsTCCA5mgAwIBAgIQBOHnpNxc8vNtwCtCuF0VnzANBgkqhkiG9w0BAQsFADBs
...
cPUeybQ=
-----END CERTIFICATE-----
(Note: this will change when the intermediate cert changes, if this isn't exactly the value you're seeing, don't freak out)
Neat! Seems like this should work. Let's throw all this logic into a script with some basic case handling (like if the cert is in a different form, then we should interpret it as that form) and we should be done, right?
#! /bin/bash
cert_content=$(curl -s "$1")
echo "$cert_content"
if echo "$cert_content" | openssl x509 -text -noout; then
echo "Not converting"
echo "$cert_content"
else
echo "Converting"
echo "$cert_content" | openssl x509 -inform DER -outform PEM
fi
What does this output when we feed the url?
...
[binary contents of a certificate]
...
unable to load certificate
4667577964:error:09FFF06C:PEM routines:CRYPTO_internal:no start line:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.230.1/libressl-2.6/crypto/pem/pem_lib.c:683:Expecting: TRUSTED CERTIFICATE
Converting
unable to load certificate
4421875308:error:0DFFF08E:asn1 encoding routines:CRYPTO_internal:not enough data:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.230.1/libressl-2.6/crypto/asn1/a_d2i_fp.c:251:
So it's pretty easy to see that the certificate is in DER form (because of the "Converting" line), but it looks like the certificate is not complete - note the error:
unable to load certificate
4421875308:error:0DFFF08E:asn1 encoding routines:CRYPTO_internal:not enough data:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.230.1/libressl-2.6/crypto/asn1/a_d2i_fp.c:251:
says that there is not enough data.
Why is this different between the script and the actions I'm taking in the shell? How can I feed the binary cert through the openssl tool to get the PEM-form cert out?
As the question alludes, I was not strict enough in my shell usage. The "interactive" session was done using zsh, and the script was run under bash. zsh will append an EOF to variables passed through its pipes, while bash (apparently) does not. Now my quest becomes how to append an EOF to a variable in bash (which, apparently, is non-trivial).

Generate OpenSSL subject_name hash in ruby

I've investigated 30261296 however I'm still at a loss to find a way to generate the same results in Ruby with the openssl and/or digest gems. The OpenSSL output I'm trying to replicate in ruby is as follows:
$ openssl x509 -noout -subject_hash -in DigiCertSHA2SecureServerCA.pem
85cf5865
In reading many things, I believe this hash is generated from the Subject: portion of the certificate, which is like the distinguished name. In this certificates case something to the effect of:
$ openssl x509 -noout -subject -in DigiCertSHA2SecureServerCA.crt
subject=C = US, O = DigiCert Inc, CN = DigiCert SHA2 Secure Server CA
Attempting to SHA-1 encode that on the command line or in Ruby (which represents this as /C=US,/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA when using the openssl gem) has not yeilded the same has results displayed by OpenSSL.
I'm trying to do this more natively in Ruby to avoid shelling out to openssl if possible since openssl and digest come along with the ruby env. In the end I need this to generate the hash directory tree ... i.e. 85cf5865.0 (hash + '.0').
The CA I'm hasing is DigiCertSHA2SecureServerCA.crt - DER encoded. I converted DER to PEM because openssl command line uses that without the additional -inform der switch. It doesn't appear to matter to Ruby's openssl gem.
This turns out to be pretty straightforward, since Ruby’s OpenSSL bindings includes the OpenSSL::X509::Name#hash method, which is exactly what we want.
require 'openssl'
# Read the certificate.
cert = OpenSSL::X509::Certificate.new(File.binread("DigiCertSHA2SecureServerCA.crt"))
# Get the subject, which is an OpenSSL::X509::Name object.
name = cert.subject
# hash returns an integer, we want the hex string so call to_s(16).
puts name.hash.to_s(16) #=> 85cf5865
The integer will be positive since OpenSSL returns an unsigned int, so we can just use to_s(16) and don’t need to worry about converting negative values.

Makecert: a certificate basic constraints extension has not been observed

I'm trying to create a self sign certificate by makecert Here is what I do:
makecert -n "CN=TuyenTk CA, C=VN, ST=Hanoi, L=Hoan Kiem" -cy authority
-h 1 -a sha1 -sv "D:\TuyenTk CA.pvk" -r "D:\TuyenTk CA.cer"
makecert -n "CN=TuyenTk" -ic "D:\TuyenTk CA.cer" -iv "D:\TuyenTk CA.pvk"
-eku "1.3.6.1.5.5.7.3.3" -cy end -a sha1 -h 0 -sky exchange -pe "D:\TuyenTk.cer"
pvk2pfx -pvk "D:\TuyenTk CA.pvk" -spc "D:\TuyenTk CA.cer"
-pfx "D:\TuyenTk.pfx" -pi "myPassWord"
The first line is make self sign cert (CA cert), The second line is use CA cert sign other cert, and the last is generate pfx file to sign the exe file.
Though all above 3 commands is reported success, when I double click to TuyenTk CA.cer and TuyenTk.cer, in the Details tab windows tell that the basic constraints is critical. So when I use the TuyenTk.pfx file to sign my exe file, in the Digital Signatures Tab, the certificate is not valid: a certificate basic constraints extension has not been observed
I view cert's details before install it, and after install in trusted root or personal location of cert store, I still see the error.
How can I fix this problem? Thank!
To create your self-signed root CA certificate, try these options:
makecert -r -pe -m 1200 -len 2048 -n "CN=TuyenTk CA, C=VN, ST=Hanoi, L=Hoan Kiem" -ss CA -sr CurrentUser -a sha1 -sky signature -cy authority -sv "D:\TuyenTk_CA.pvk" "D:\TuyenTk_CA.cer"
I left off "-h 1" to give you unlimited signing depth in the basic constraints; some SSL packages don't like unlimited path lengths, so you can either have layers of keys or put in "-h 5" or whatever value you feel will serve your needs. Switches I added:
-pe Make private key exportable
-m 1200 Make CA key valid for 100 years (1200 months)
-ss CA This key goes into the CA certificate store
-sr CurrentUser Certificate store location
-sky signature Key type (use for signing)
I also added an underscore (instead of a blank) in the name; may not be necessary, but my certificate files do not have spaces (these utilities can be odd sometimes).
When you import the CA certificate, make sure you do so into the "Trusted Root Certification Authorities\Local Computer" physical store location. For instance, use this from an Admin cmd prompt:
certutil -addstore -v root "D:\TuyenTk_CA.cer"
These steps worked for me on XP and work today on Windows 7. Hope this helps!
drac

Ruby OpenSSL, print issuer and subject of pem file

How do I print (or save to variable) the Issuer and Subject from a .pem certificate using the OpenSSL module ?
(This is after trying to understand the ruby-docs)
I used this System-depended and ugly code, but I'm sure there is a much nicer thing to do
pfxsubject = %x(openssl x509 -in '/root/cert.pem' -noout -subject | cut -c 10-).to_s.chomp

How to specify passphrases for P12 to PEM file conversion without interaction.

I'm trying to convert a P12 file to a PEM file. When I execute the command, the terminal asks me for three things:
P12 passphrase (I type it in, hit enter)
PEM passphrase (type it in, hit enter)
PEM passphrase confirm (type it in, hit enter)
I know I can execute a sudo command all in one shot by using the following:
echo sudopassword | sudo rm -rf /file.p12;
How can I add all three values in one shot? Thanks
Can you explain what these P12 files are? I found this link which deals with the conversion of pkcs12 Cert/key files to .PEM format using openssl. (http://gridsite.org)
Key to the answer is:
Use -passin file:... and -passout file:... for unattended processing
It's my guess that you will have to specify the -passin file:P12passphrase and -passout file PEMpassphrase options for this case.
This little test confirms how an input passphrase can be specified through a file:<...> parameter. This helps to hide such phrases from any over the shoulder attacks. Don't forget to restrict access to such files. Even though it's a common feature of most openssl commands, it's not explicitly mentioned and it is key to the original question. The full list of options is below.
$ openssl pkcs12 -passin file:P12phrase
Can't open file P12phrase
Error getting passwords
(I leave it to the OP to construct the full command.)
Below are all supported options for the pkcs12 subcommand:
$ openssl pkcs12 help
Usage: pkcs12 [options]
where options are
-export output PKCS12 file
-chain add certificate chain
-inkey file private key if not infile
-certfile f add all certs in f
-CApath arg - PEM format directory of CA's
-CAfile arg - PEM format file of CA's
-name "name" use name as friendly name
-caname "nm" use nm as CA friendly name (can be used more than once).
-in infile input filename
-out outfile output filename
-noout don't output anything, just verify.
-nomacver don't verify MAC.
-nocerts don't output certificates.
-clcerts only output client certificates.
-cacerts only output CA certificates.
-nokeys don't output private keys.
-info give info about PKCS#12 structure.
-des encrypt private keys with DES
-des3 encrypt private keys with triple DES (default)
-aes128, -aes192, -aes256
encrypt PEM output with cbc aes
-nodes don't encrypt private keys
-noiter don't use encryption iteration
-maciter use MAC iteration
-twopass separate MAC, encryption passwords
-descert encrypt PKCS#12 certificates with triple DES (default RC2-40)
-certpbe alg specify certificate PBE algorithm (default RC2-40)
-keypbe alg specify private key PBE algorithm (default 3DES)
-keyex set MS key exchange type
-keysig set MS key signature type
-password p set import/export password source
-passin p input file pass phrase source
-passout p output file pass phrase source
-engine e use engine e, possibly a hardware device.
-rand file:file:...
load the file (or the files in the directory) into
the random number generator
-CSP name Microsoft CSP name
-LMK Add local machine keyset attribute to private key
It's unlikely that these commands are reading from stdin. It's more likely that they're reading directly from the terminal. This allows them to set a mode that doesn't echo the password to the screen. Try echoing your input to /dev/tty.
Beyond that, you'll need to use something like expect / pexect to control these. Those projects were build specifically for this purpose.
Openssl has a -stdin optoin to read its input from stdin. This works:
tmp=`mktemp`
cat > $tmp <<EOF
$1
EOF
cat $tmp | openssl req -out CSR.csr -new -newkey rsa:2048 -nodes -keyout privateKey.key
I've used cat and a here-document to avoid putting the password on the commandline.
I used openssl pkcs12 -in Certificates.p12 -out sampleCore.pem -nodes and it was working for me.
Have you tried just echoing three lines? It would probably work
echo $'P12 passphrase\nPEM passphrase\nPEM passphrase confirm' | cmd
Although I feel I must point out that echoing passwords like this is highly insecure. Not only does the password end up in your bash history file, but it's also visible to anyone else on the system who runs ps.

Resources