I used this bash script here to download only short parts of long yt videos:
#!/bin/bash
#taken from https://unix.stackexchange.com/a/388148/48971
if [ $# -lt 4 ]; then
echo "Usage: $0 <youtube's URL> <HH:mm:ss.milisecs from time> <HH:mm:ss.milisecs to time> <output_file_name>"
echo "e.g.:"
echo "$0 https://www.youtube.com/watch?v=T1n5gXIPyws 00:00:25 00:00:42 intro.mp4"
exit 1
fi
echo "processing..."
from=$(date "+%s" -d "UTC 01/01/1970 $2")
to=$(date "+%s" -d "UTC 01/01/1970 $3")
from_pre=$(($from - 30))
if [ $from_pre -lt 0 ]
then
from_pre=0
fi
from_pre_command_print=$(date -u "+%T" -d #$from_pre)
from_command_print=$(date -u "+%T" -d #$(($from - $from_pre)))$(grep -o "\..*" <<< $2)
to_command_print=$(date -u "+%T" -d #$(($to - $from_pre)))$(grep -o "\..*" <<< $3)
command="ffmpeg "
for uri in $(youtube-dl -g $1)
do
command+="-ss $from_pre_command_print -i $uri "
done
command+="-ss $from_command_print -to $to_command_print $4"
echo "downloading with the following command:"
echo "$command"
$command
but the problem is its only precise to the full second.
I want to use it to download a bunch of very short clips (only one word long mostly) those are often below 1 second long.
I tryed to fix this by using milliseconds in the date.
But then i found out bash can only subtract with integers.
Here is what i tryed:
from=$(date "+%s.%3N" -d "UTC 01/01/1970 $2")
to=$(date "+%s.%3N" -d "UTC 01/01/1970 $3")
function diff {
diff="$(echo $from - 5 | bc)"
echo $diff
}
from_pre=$diff
echo $diff
but the workaround using bc didnt work because later in the script its again throwing errors because bash dosnt know what to do with non integers.
a sample command would look like this:
sh download_youtube.sh https://www.youtube.com/watch?v=dH3auOKyxio 00:06:28.230 00:06:28.740 clip004.mp4
and this works...if the the timeframe is more than 1 second.
Sadly i am out of knowlage here how to make it more precise.
This all is part of a project to make automated compilations of specific word from a youtube channel more on that here:
https://github.com/moeC137/video-recutter
I am very thankfull for every help
edit:
after a bit try an error i found out that this seams to work for me:
#!/bin/bash
#taken from https://unix.stackexchange.com/a/388148/48971
if [ $# -lt 4 ]; then
echo "Usage: $0 <youtube's URL> <HH:mm:ss.milisecs from time> <HH:mm:ss.milisecs to time> <output_file_name>"
echo "e.g.:"
echo "$0 https://www.youtube.com/watch?v=T1n5gXIPyws 00:00:25 00:00:42 intro.mp4"
exit 1
fi
echo "processing..."
from=$(date "+%s" -d "UTC 01/01/1970 $2")
to=$(date "+%s" -d "UTC 01/01/1970 $3")
from_pre=$(($from - 20))
#to_post=$(($to + 20))
if [ $from_pre -lt 0 ]
then
from_pre=0
fi
from_pre_command_print=$(date -u "+%T" -d #$from_pre)
from_command_print=$(date -u "+%T" -d #$(($from - $from_pre)))$(grep -o "\..*" <<< $2)
to_command_print=$(date -u "+%T" -d #$(($to - $from_pre)))$(grep -o "\..*" <<< $3)
#to_post_command_print=$(date -u "+%T.%3N" -d #$to_post)
command="ffmpeg "
for uri in $(youtube-dl -g $1)
do
command+="-ss $from_pre_command_print -i $uri "
done
command+="-ss $from_command_print -to $to_command_print $4"
echo "downloading with the following command:"
echo "$command"
$command
But i starting to think the orginal script actually works with milleseconds.
Because this:
sh download_youtube.sh https://www.youtube.com/watch?v=dH3auOKyxio 00:06:28.230 00:06:28.740 clip004.mp4
works, but when try to combine it with the script from here How to repeat a command for every line in a textfile with given arguments from the textfile (Bash?) it dosent use the milliseconds anymore.
Solved
The orginal Script worked. It just didnt like the "," in the timestamps, i replaced them with "." and now its working.
solved by replacing "," in timestamps with "."
can someone help me to finish a script in bash for check openssl certificates and send mail before expire ? I tried with some code from here, but i don't know exactly how to continue
location=/home/merox/Desktop/*.pem ;
server=$HOSTNAME;
for pem in $location; do
printf '%s: %s\n' \
certexpire=$(date -d "$(: | openssl x509 -enddate -noout -in "$pem"|cut -d= -f 2)" --iso-8601) \
"$pem"
done | sort
OUTPUT:
certexpire=2019-05-25: /home/merox/Desktop/key_me.pem
certexpire=2019-05-25: /home/merox/Desktop/key_merox.pem
certexpire=2021-07-14: /home/merox/Desktop/cert_me.pem
Comments in code.
# So, let's take the files from find and save them in an array
# Using globulation '*' is less secure.
IFS='\n' files=($(find /home/merox/Desktop -mindepth 1 -maxdepth 1 -name '*.pem'))
# one week in seconds
one_week=$((7 * 24 * 60 * 60))
# current time in seconds since epoch
now=$(date "+%s")
# for each file we want to check
for pem in "${files[#]}"; do
# They expire at this time in seconds since epoch
expires_at=$(date -d "$(: | openssl x509 -enddate -noout -in "$pem"|cut -d= -f 2)" +%s)
# the difference
expires_in=$((expires_at - now))
# if the will expire in less then one_week
if (( expires_in < one_week )); then
# just print them
printf "%s\n" "$pem"
fi
done |
sort |
# I leave it to you on how to configure sendmail on your PC
sendmail -v "name#mail.com"
I have written a bash script to automate IIS7 Certificate generation as per this ServerFault link.
I would like to automate sending the Code Signing Request (CSR) to an internal Microsoft Certification Authority (MS CA) via cURL, the following code is promising and is successfully submitting the CSR to MS CA:
$ curl -k -u '<Domain>\<Username>':<Password> --ntlm
'https://<InternalMSCA>/certsrv/certfnsh.asp'
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
-H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: en-US,en;q=0.5'
-H 'Connection: keep-alive'
-H 'Host: <InternalMSCA>'
-H 'Referer: https://<InternalMSCA>/certsrv/certrqxt.asp'
-H 'User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko'
-H 'Content-Type: application/x-www-form-urlencoded'
--data 'Mode=newreq&CertRequest=-----BEGIN+CERTIFICATE+REQUEST-----%0D%0AMIIDBjCCAe4CAQAwaDELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UE%0D%0ABxMGU3lkbmV5MQwwCgYDVQQKEwNZdW0xDjAMBgNVBAsTBVl1bUlTMRwwGgYDVQQD%0D%0AExN0ZXN0LmF1LmludC50Z3IubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB%0D%0ACgKCAQEAygZvKhfs0mw4tModevTxOIz7eYYM%2B1axNv8FqoNyKr7xtqSbOMiNzf8R3rZ%0D%0A4cTcu5nv7oC7GHPMhnF7AdsO4XexwnKfnCkofECGkO6O4oTmRfUPLa38nV1%2BmytB%0D%0AlrQAl272jQdM9LSxTYW0OR9qO4mjAH1tvLF3IcC1OKOh6UNubdRFfE7dEXWnk%2BSF%0D%0AM8tgl0t3SFsRxrZL3vkgL%2B%2FEmvdOKXeoIey%2F7UMNeWRcwTkS1mw30HjvitJdQGZi%0D%0AgYJ6ldXrrITVKe9QXvVTxSl9NfzPHYp4yf%2FZvAJQmGLZ16aQo0PBeEfjkgkrcY5j%0D%0AMnVI2Q8yC%2BW9Bg%3D%3D%0D%0A-----END+CERTIFICATE+REQUEST-----&CertAttrib=CertificateTemplate%3A*WebServer%0D%0AUserAgent%3AMozilla%2F5.0+%28Windows+NT+6.3%3B+WOW64%3B+Trident%2F7.0%3B+rv%3A11.0%29+like+Gecko%0D%0A&FriendlyType=Saved-Request+Certificate+%287%2F7%2F2015%2C+3%3A46%3A39+PM%29&ThumbPrint=&TargetStoreFlags=0&SaveCert=yes'
| firefox "data:text/html;base64,$(base64 -w 0 <&0)"
I am interested in replaying this request after modifying it:
Decode --data (OK)
Modify --data (OK)
Re-encode... (Not OK)
Encoded:
Mode=newreq&CertRequest=-----BEGIN+CERTIFICATE+REQUEST-----%0D%0AMIIDBjCCAe4CAQAwaDELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UE%0D%0ABxMGU3lkbmV5MQwwCgYDVQQKEwNZdW0xDjAMBgNVBAsTBVl1bUlTMRwwGgYDVQQD%0D%0AExN0ZXN0LmF1LmludC50Z3IubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB%0D%0ACgKCAQEAygZvKhfs0mw4tModevTxOIz7eYYM%2B1axNv8FqoNyKr7xtqSbOMiNzf8R3rZ%0D%0A4cTcu5nv7oC7GHPMhnF7AdsO4XexwnKfnCkofECGkO6O4oTmRfUPLa38nV1%2BmytB%0D%0AlrQAl272jQdM9LSxTYW0OR9qO4mjAH1tvLF3IcC1OKOh6UNubdRFfE7dEXWnk%2BSF%0D%0AM8tgl0t3SFsRxrZL3vkgL%2B%2FEmvdOKXeoIey%2F7UMNeWRcwTkS1mw30HjvitJdQGZi%0D%0AgYJ6ldXrrITVKe9QXvVTxSl9NfzPHYp4yf%2FZvAJQmGLZ16aQo0PBeEfjkgkrcY5j%0D%0AMnVI2Q8yC%2BW9Bg%3D%3D%0D%0A-----END+CERTIFICATE+REQUEST-----&CertAttrib=CertificateTemplate%3A*WebServer%0D%0AUserAgent%3AMozilla%2F5.0+%28Windows+NT+6.3%3B+WOW64%3B+Trident%2F7.0%3B+rv%3A11.0%29+like+Gecko%0D%0A&FriendlyType=Saved-Request+Certificate+%287%2F7%2F2015%2C+3%3A46%3A39+PM%29&ThumbPrint=&TargetStoreFlags=0&SaveCert=yes
Decoded:
Mode=newreq&CertRequest=-----BEGIN CERTIFICATE REQUEST-----
MIIDBjCCAe4CAQAwaDELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UE
BxMGU3lkbmV5MQwwCgYDVQQKEwNZdW0xDjAMBgNVBAsTBVl1bUlTMRwwGgYDVQQD
ExN0ZXN0LmF1LmludC50Z3IubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAygZvKhfs0mw4tModevTxOIz7eYYM+1axNv8FqoNyKr7xtqSbOMiNzf8R3rZ
4cTcu5nv7oC7GHPMhnF7AdsO4XexwnKfnCkofECGkO6O4oTmRfUPLa38nV1+mytB
lrQAl272jQdM9LSxTYW0OR9qO4mjAH1tvLF3IcC1OKOh6UNubdRFfE7dEXWnk+SF
M8tgl0t3SFsRxrZL3vkgL+/EmvdOKXeoIey/7UMNeWRcwTkS1mw30HjvitJdQGZi
gYJ6ldXrrITVKe9QXvVTxSl9NfzPHYp4yf/ZvAJQmGLZ16aQo0PBeEfjkgkrcY5j
MnVI2Q8yC+W9Bg==
-----END CERTIFICATE REQUEST-----&CertAttrib=CertificateTemplate:*WebServer
UserAgent:Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0)
like Gecko &FriendlyType=Saved-Request Certificate (7/7/2015, 3:46:39
PM)&ThumbPrint=&TargetStoreFlags=0&SaveCert=yes
Re-encoded: (URLEncode1, URLEncode2, URLEncode3 ):
Mode%3Dnewreq%26CertRequest%3D-----BEGIN+CERTIFICATE+REQUEST-----+MIIDBjCCAe4CAQAwaDELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UE+BxMGU3lkbmV5MQwwCgYDVQQKEwNZdW0xDjAMBgNVBAsTBVl1bUlTMRwwGgYDVQQD+ExN0ZXN0LmF1LmludC50Z3IubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB+CgKCAQEAygZvKhfs0mw4tModevTxOIz7eYYM%2B1axNv8FqoNyKr7xtqSbOMiNzf8R3rZ+4cTcu5nv7oC7GHPMhnF7AdsO4XexwnKfnCkofECGkO6O4oTmRfUPLa38nV1%2BmytB+lrQAl272jQdM9LSxTYW0OR9qO4mjAH1tvLF3IcC1OKOh6UNubdRFfE7dEXWnk%2BSF+M8tgl0t3SFsRxrZL3vkgL%2B%2FEmvdOKXeoIey%2F7UMNeWRcwTkS1mw30HjvitJdQGZi+gYJ6ldXrrITVKe9QXvVTxSl9NfzPHYp4yf%2FZvAJQmGLZ16aQo0PBeEfjkgkrcY5j+MnVI2Q8yC%2BW9Bg%3D%3D+-----END+CERTIFICATE+REQUEST-----%26CertAttrib%3DCertificateTemplate%3A%2AWebServer+UserAgent%3AMozilla%2F5.0+%28Windows+NT+6.3%3B+WOW64%3B+Trident%2F7.0%3B+rv%3A11.0%29+like+Gecko+%26FriendlyType%3DSaved-Request+Certificate+%287%2F7%2F2015%2C+3%3A46%3A39+PM%29%26ThumbPrint%3D%26TargetStoreFlags%3D0%26SaveCert%3Dyes
The three websites linked above (in Re-encoded) all fail to re-encode properly.
The tricky part is that "=" and "&" should not be encoded.
URL Encode Simple:
CR LF %0D%0A (Not %)
Space + (Not %20)
- - (Not %2D)
& & (Not %26)
= = (Not %3D)
+ (in CSR) %2B
\ (in CSR) %2F
( %28
) %29
I could specifically use sed for this example, but I would like to know if there is a way to know what encoding the server is expecting, and encode in the proper charset automatically. Is this possible ?
I solved in linux on bash and curl:
#!/bin/sh
# tested on SUSE Linux 12 SP1
# $1 - CN Object name
# $2 - username
# $3 - password
MSCA='HOSTNAME' # Internal Microsoft Certification Authority
Username=$2
Password=$3
function show_usage()
{
echo "Scrip for retrive certificate from MS SubCA"
echo "Usage: $0 <CN> [domain\\\\username] [password]"
echo " "
echo "Example: $0 example.com workgroup\\\\foo bar"
exit 0
}
if [ -z "$1" ]
then
show_usage
exit 0
fi
if [ -z "$2" ]
then
Username="workgroup\\foo"
Password="bar"
fi
echo -e "\e[32m1. Generate private key...\e[0m"
openssl req -new -nodes -out $1.pem -keyout $1.key -subj "/C=RU/ST=State/L=City/O=Org/CN=$1/emailAddress=postmaster#example.com"
CERT=`cat $1.pem | tr -d '\n\r'`
DATA="Mode=newreq&CertRequest=${CERT}&C&TargetStoreFlags=0&SaveCert=yes"
CERT=`echo ${CERT} | sed 's/+/%2B/g'`
CERT=`echo ${CERT} | tr -s ' ' '+'`
CERTATTRIB="CertificateTemplate:Server%0D%0A"
echo -e "\e[32m2. Request cert...\e[0m"
OUTPUTLINK=`curl -k -u "${Username}":${Password} --ntlm \
"https://${MSCA}/certsrv/certfnsh.asp" \
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \
-H 'Accept-Encoding: gzip, deflate' \
-H 'Accept-Language: en-US,en;q=0.5' \
-H 'Connection: keep-alive' \
-H "Host: ${MSCA}" \
-H "Referer: https://${MSCA}/certsrv/certrqxt.asp" \
-H 'User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data "Mode=newreq&CertRequest=${CERT}&CertAttrib=${CERTATTRIB}&TargetStoreFlags=0&SaveCert=yes&ThumbPrint=" | grep -A 1 'function handleGetCert() {' | tail -n 1 | cut -d '"' -f 2`
CERTLINK="https://${MSCA}/certsrv/${OUTPUTLINK}"
echo -e "\e[32m3. Retrive cert: $CERTLINK\e[0m"
curl -k -u "${Username}":${Password} --ntlm $CERTLINK \
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \
-H 'Accept-Encoding: gzip, deflate' \
-H 'Accept-Language: en-US,en;q=0.5' \
-H 'Connection: keep-alive' \
-H "Host: ${MSCA}" \
-H "Referer: https://${MSCA}/certsrv/certrqxt.asp" \
-H 'User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko' \
-H 'Content-Type: application/x-www-form-urlencoded' > $1.crt
echo -e "\e[32m4. Verifying cert for $1\e[0m"
openssl verify -verbose $1.crt
if [ "0" -eq "$?" ] ;
then
echo -e "\e[32mWell done. Have a nice day.\e[0m"
exit 0
else
echo -e "\e[31;47mError code: $?. Stopping.\e[0m"
exit 1
fi
Here's the bash script on Windows (CYGWIN):
#!/bin/bash
#############################################################################################
# AUTHOR: FlORIAN BIDABE #
# #
# VERSION 2.0 RELEASE DATE February 07, 2018 #
# This script helps you with generating SSL material from an internal Micorosft CA #
# 1) Define your variables and CA Bundle in this script (between < and >) #
# 2) Run the script #
# #
# Process: #
# 1- Generate or import CSR #
# 2- Submit CSR and specify additional Subject Alternate Name (SAN) #
# 3- Collect certificate from your CA (Certificate Authority) ==> MANUAL #
# 4- Generate SSL material and format #
# #
# #
# Tested on: #
# Certificate Authority: Windows Server 2008 R2 / 2012 #
# Client: Windows 8.1 and Windows 10 with cygwin (cURL, OpenSSL, clip) #
#############################################################################################
#_____________________________________________________________________________________________
######################################## Variables ########################################
# Internal Env Settings
MSCA='server.domain.tld' # Internal Microsoft Certification Authority FQDN
CertTplt='WebServer' # Internal Cert Template Name
UA='Mozilla%2F5.0+%28Windows+NT+6.3%3B+WOW64%3B+Trident%2F7.0%3B+rv%3A11.0%29+like+Gecko'
Domain='localdomain' # Used for signing both hostname and FQDN
Username=""$Domain"\\userid" # Required for certificate submission
#Password='WH4t3v3r' # Can be commented to be interactive
# Email Settings (Unauthenticated in my case)
mailserver="mailserver.com" # Mailserver FQDN
mailport=25 # Might need SSL if 465, 587
to="ServiceDesk#company.com"
cc="who.ever#company.com"
bcc="yourself#company.com,fortrackingpurpose#company.com"
MailTemplate="
Hello,
Please create and assign a ticket to track this certificate request from our market.
The Request ID has been attached in the email (HTTP Response)
Date: `date "+%Y-%m-%d %H:%M"`
Issuer: "$Username"
Information Systems
Phone number
Company Pty Ltd
Address and contact details"
# SaveIn=~/Desktop/Certs
SaveIn=~/Certificates/NewRequests # Save the file in Team's OneDrive folder mapped with ln using cygwin
FileMgr=explorer # File Manager
# OpenSSL CFG settings for CSR (Code Signing Request) submission
Country='AU'
State='NSW'
City='Sydney'
Company='Company'
UrOrg='Information Systems'
# Internal Base64 Root and Intermediate CAs (Used for creating PEM and PKCS12 bundles)
IntRoot=`echo '
-----BEGIN CERTIFICATE-----
<Intermediate>
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
<Root>
-----END CERTIFICATE-----'`
#_____________________________________________________________________________________________
######################################## Requirements ########################################
# OpenSSL
type openssl > /dev/null 2>&1 || {
echo "Cannot find OpensSSL, it is required to generate certificates. Aborting..." 1>&2
exit 1
}
# cURL
type curl > /dev/null 2>&1 || {
echo "Cannot find cURL, it is required to submit certificates. Aborting..." 1>&2
exit 1
}
#_____________________________________________________________________________________________
######################################## Optional ############################################
# Clip
type clip > /dev/null 2>&1 || {
echo -e "Cannot find clip ! it is required to save the CSR into your clipboard.\n Attempting to install it in System32..." 1>&2
cd 'C:\Windows\system32'; curl -L -O "https://www.dropbox.com/s/cvkxeak0j0wtjj0/clip.exe"
}
# GNU Email
type email > /dev/null 2>&1 || {
echo -e "Cannot find GNU email ! it is required to send an email to notify a security administrator and issue the certificate." 1>&2
}
# Internet Explorer
if [ -f '/cygdrive/c/Program\ Files/Internet\ Explorer/iexplore.exe' ]; then iexplore='/cygdrive/c/Program\ Files/Internet\ Explorer/iexplore.exe'
else iexplore=$(sed 's| |\\ |g' <<< "$(find /cygdrive/ -name "iexplore.exe" -exec sh -c 'printf "%s\n" "$1"; kill "$PPID"' bash {} \;)")
fi
#_____________________________________________________________________________________________
######################################## Functions ########################################
gencsr() {
# Generate Config File (CFG) for Code Signing Request (CSR)
echo "`date "+%Y-%m-%d %H:%M:%S"` - User Option: 1) Generate CSR and Private Key" >> $LOGS
echo "`date "+%Y-%m-%d %H:%M:%S"` - Parsing Config File (CFG)" >> $LOGS
# Set additional SAN (for CFG)
local n=1 #Enter Loop
local SAN
SAN="subjectAltName = DNS:"$Hostname", DNS: "$Hostname.$Domain""
while (( n > 0 && n < 4 )); do
echo -e "\n\n\nDo you want to set an additional Subject Alternate Name (Config File) ? (No)"
echo -e "Current SAN:\n"$SAN""
echo -e "Select your choice and press [ENTER]\n\t[1] Add an IP address\n\t[2] Add an hostname\n\t[3] Reset SAN to default\n\t[*] Continue"
read -p "Option number : " n
case $n in
1) # Add Extra IP for SAN
while [[ -z ${IP+x} || $? != 0 ]]; do
read -p "What is the server's IP address: " IP
[[ "$IP" =~ ^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$ ]]
if [ $? != 0 ]; then echo "This IP address ("$IP") does not look quite right! Please try again..."; fi
[[ "$IP" =~ ^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$ ]]
done
SAN+=", IP:"$IP", DNS:"$IP""; unset IP
;;
2) # Add extra DNS name to SAN
while [[ -z ${extraSAN+x} || $? != 0 ]]; do
read -p "Specify a Fully Qualified Domain Name for the extra SAN : " extraSAN
[[ "$extraSAN" =~ ^[A-Za-z0-9.-]+$ ]]
if [ $? != 0 ]; then echo "This syntax is incorrect! Please try again..."; fi
[[ "$extraSAN" =~ ^[A-Za-z0-9.-]+$ ]]
done
SAN+=", DNS:"$extraSAN""; unset extraSAN
;;
3) SAN="subjectAltName = DNS:"$Hostname", DNS:"$Hostname.$Domain"" ;;
*) n=4 ;; #Quit loop
esac
done
echo "`date "+%Y-%m-%d %H:%M:%S"` - Subject Alternate Name (CFG): "$SAN"" >> $LOGS
echo "
[ req ]
default_md = sha512
default_bits = 2048
default_keyfile = "$Hostname"_pk8.key
distinguished_name = req_distinguished_name
encrypt_key = no
prompt = no
string_mask = nombstr
req_extensions = v3_req
input_password = password
output_password = password
[ v3_req ]
basicConstraints = CA:false
keyUsage = digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth, clientAuth
"$SAN"
[ req_distinguished_name ]
countryName = "$Country"
stateOrProvinceName = "$State"
localityName = "$City"
0.organizationName = "$Company"
organizationalUnitName = "$UrOrg"
commonName = "$Hostname.$Domain"" > "$Hostname".cfg
echo "`date "+%Y-%m-%d %H:%M:%S"` - Config File (CFG) parsed ! Located at `pwd`/"$Hostname".cfg" >> $LOGS
# Generate CSR and private key (PKCS8) & convert Private Key (PKCS8 to PKCS1)
echo -e "\n\nGenerating Code Signing Request (CSR) and Private Key (PKCS#8)..."
echo "`date "+%Y-%m-%d %H:%M:%S"` - Generating Code Signing Request (CSR) and Private Key (PKCS#8): "$Hostname".csr and "$Hostname"_pk8.key" >> $LOGS
openssl req -out "$Hostname".csr -new -nodes -config "$Hostname".cfg > /dev/null 2>&1
echo "Generating private key (PKCS#1)..."
echo "`date "+%Y-%m-%d %H:%M:%S"` - Generating Private Key (PKCS#1): "$Hostname"_pk1.key" >> $LOGS
openssl rsa -in "$Hostname"_pk8.key -out "$Hostname"_pk1.key > /dev/null 2>&1
if [ $? != 0 ]; then
echo "An error has occured ! Exiting..., Please consult the logs"
echo "`date "+%Y-%m-%d %H:%M:%S"` - Error on generating CSR or Private Keys" >> $LOGS
exit 1
fi
}
importcsr() {
# Importing Code Signing Request (CSR)
echo "`date "+%Y-%m-%d %H:%M:%S"` - User Option: 2) Import CSR" >> $LOGS
local n
printf "\033c"
echo -e "This function automates IIS7 certificate generation for "$Company $UrOrg"
\tServer name:\t"$Hostname"\n\tFQDN:\t\t"$Hostname"."$Domain"\n"
echo "Importing Code Signing Request..."
#Verify CSR
# If CSR is not Base 64
openssl req -text -noout -verify -in *.csr > /dev/null 2>&1
while [ $? != 0 ]; do
# Check if there are multiple csr files
while [ $(find -name "*.csr" | wc -l) != 1 ]; do
echo -e "\nError, $(find -name "*.csr" | wc -l) CSR(s) found ! One CSR is required..."
echo "Please import your CSR in "$SaveIn" and make sure the extension is *.csr"
echo "`date "+%Y-%m-%d %H:%M:%S"` - WARNING: There should be one CSR only in "$SaveIn"" >> $LOGS
$FileMgr . 2> /dev/null
read -p "Press any key to continue...";
done
openssl req -text -noout -verify -inform DER -in *.csr > /dev/null 2>&1
if [ $? == 0 ]; then
echo -e "\n\nThis Code Signing Request is not a Base64 request !\nConverting DER request to Base64... Success !"
mv *.csr "$Hostname".dcsr
openssl req -out "$Hostname".csr -outform PEM -inform DER -in *.dcsr
echo "`date "+%Y-%m-%d %H:%M:%S"` - DER CSR detected, converting to Base64... Success !" >> $LOGS
echo "`date "+%Y-%m-%d %H:%M:%S"` - DER CSR: "$Hostname".dcsr\tBase64 CSR: "$Hostname".csr" >> $LOGS
else
openssl req -text -noout -verify -in *.csr > /dev/null 2>&1
if [ $? != 0 ]; then
echo -e "Your CSR file is not valid or is corrupted!\nPlease import your CSR in "$SaveIn"..."
echo "`date "+%Y-%m-%d %H:%M:%S"` - ERROR: This CSR is invalid, it is neither a DER or Base64 CSR" >> $LOGS
$FileMgr . 2> /dev/null
read -p "Press any key to continue..."; fi
fi
openssl req -text -noout -verify -in *.csr > /dev/null 2>&1
done
# Optional: Converting a Base64 CSR to DER
if [ ! -f *.dcsr ]; then
openssl req -outform DER -inform PEM -in *.csr -out "$Hostname".dcsr > /dev/null 2>&1
if [ $? == 0 ]; then
echo "`date "+%Y-%m-%d %H:%M:%S"` - Base64 CSR detected, converting to DER... Success !" >> $LOGS
echo "`date "+%Y-%m-%d %H:%M:%S"` - DER CSR: "$Hostname".dcsr\tBase64 CSR: "$Hostname".csr" >> $LOGS
fi
fi
}
urlencode() {
local data
if [[ $# != 1 ]]; then return 1; fi
data="$(curl -s -o /dev/null -w %{url_effective} --get --data-urlencode "$1" "")"
if [[ $? != 3 ]]; then return 2; fi
echo "${data##/?}"; return 0
}
getcert() {
######################### 3- Get Certificate ########################
echo -e "\n\n`date "+%Y-%m-%d %H:%M:%S"` - Step 3: Getting the Certifiate" >> $LOGS
printf "\033c"
echo -e "This function automates IIS7 certificate generation for "$Company $UrOrg"
\tServer name:\t"$Hostname"\n\tFQDN:\t\t"$Hostname"."$Domain"\n"
echo -e "Open \"Certificate Authority\" in a Management Console (MMC) and connect to "$MSCA"\nVerify that your certificate request is in "Pending Requests".\nIssue the Certificate (Right Click, All Tasks, Issue)\nNavigate to "Issue Certificates", order by Request ID (Descending) and export it (Open / Details / Copy To File) 'Base-64 Encoded X.509' to "$SaveIn".\nThe file must have a *.cer extension\n"
read -p "Press any keys when the certificate (*.cer) has been place in "$SaveIn""
#Verify Certificate
openssl x509 -text -noout -in "$Hostname".cer > /dev/null 2>&1
while [ $? != 0 ]; do
# Verify that there is only one certificate
while [ $(find -name "*.cer" | wc -l) == 0 ]; do
echo "Please import certificate (*.cer) in "$SaveIn""
if [ -z ${Manual+x} ]; then $FileMgr . 2> /dev/null
else
#If the certificate has been uploaded using a browser, it can be retrieved using the browser
if [ -z ${iexplore+x} ]; then echo "Open "https://"$MSCA"/certsrv/certckpn.asp""
else eval $iexplore "https://"$MSCA"/certsrv/certckpn.asp"; fi
fi
read -p "Press any key to continue..."; done
while [ $(find -name "*.cer" | wc -l) != 1 ]; do
echo "Error, $(find -name "*.cer" | wc -l) certificates found in "$SaveIn"! Please clean it up !"
$FileMgr . 2> /dev/null
read -p "Press any key to continue..."
done
# Verify Certificate Integrity and format
mv *.cer "$Hostname".cer
echo -e "`date "+%Y-%m-%d %H:%M:%S"` - Certificate found at `pwd`/"$Hostname".cer" >> $LOGS
openssl x509 -text -noout -in *.cer > /dev/null 2>&1
if [ $? != 0 ]; then
openssl x509 -inform der -text -noout -in *.cer > /dev/null 2>&1 # Test if DER
if [ $? == 0 ]; then # Convert DER to Base64
mv *.cer "$Hostname".der
openssl x509 -inform der -in "$Hostname".der -out "$Hostname".cer > /dev/null 2>&1
echo "`date "+%Y-%m-%d %H:%M:%S"` - DER certificate detected, converting to Base64... Success !" >> $LOGS
echo "`date "+%Y-%m-%d %H:%M:%S"` - DER certificate: "$Hostname".der\tBase64 certificate: "$Hostname".cer" >> $LOGS
else
echo -e "This certificate is invalid or corrupted!\nPlease import it again in "$SaveIn"..."
echo "`date "+%Y-%m-%d %H:%M:%S"` - ERROR: The certificate is invalid, it is neither a DER or Base64 certificate" >> $LOGS
read -p "Press any key to continue..."
fi
openssl x509 -text -noout -in *.cer > /dev/null 2>&1
fi
done
# Optional: Converting a Base64 CSR to DER
if [ ! -f *.der ]; then
openssl x509 -outform der -in "$Hostname".cer -out "$Hostname".der > /dev/null 2>&1
if [ $? == 0 ]; then
echo "`date "+%Y-%m-%d %H:%M:%S"` - Base64 Certificate detected, converting to DER... Success !" >> $LOGS
echo "`date "+%Y-%m-%d %H:%M:%S"` - DER Certificate: "$Hostname".dcsr\tBase64 Certificate: "$Hostname".csr" >> $LOGS
fi
fi
###################### 4- Generating SSL material #########################
# Creating PEM certificate chain
echo -e "`date "+%Y-%m-%d %H:%M:%S"` - Step 4 (Final): Generating SSL material" >> $LOGS
if [ -f ""$Hostname"_pk1.key" ]; then
cat "$Hostname"_pk1.key > ""$Hostname".pem"
cat *.cer >> ""$Hostname".pem"
echo -e "`date "+%Y-%m-%d %H:%M:%S"` - A PEM has been generated containing the Private Key and entire certificate chain: Public Key for "$Hostname" and CA Bundle (intermediate and root certificates) " >> $LOGS
else
cat *.cer > ""$Hostname".pem"
echo -e "`date "+%Y-%m-%d %H:%M:%S"` - A PEM has been generated containing the entire certificate chain: Public Key for "$Hostname" and CA Bundle (intermediate and root certificates)" >> $LOGS
echo -e "`date "+%Y-%m-%d %H:%M:%S"` - As the CSR was imported, no private key can be included in the PEM container" >> $LOGS
fi
echo "$IntRoot" >> ""$Hostname".pem"
sed -i '/^$/d' "$Hostname".pem"" # Delete empty lines
# Converting PEM certificate chain to PKCS#12 (.pfx)"
cat *.pfx 2> /dev/null #Enter Loop
while [ $? != 0 ]; do
if [ -f "$Hostname"_pk1.key ]; then openssl pkcs12 -export -out ""$Hostname".pfx" -in ""$Hostname".pem"
else openssl pkcs12 -export -nokeys -out ""$Hostname".pfx" -in ""$Hostname".pem"
fi
done
echo -e "`date "+%Y-%m-%d %H:%M:%S"` - A PKCS12 (.pfx, .p12) has been generated from the PEM" >> $LOGS
echo -e "`date "+%Y-%m-%d %H:%M:%S"` - Ending gracefully :)" >> $LOGS
mv ../"$Hostname" ../../INTERNAL/
cd ../../INTERNAL/"$Hostname"
$FileMgr . 2> /dev/null
exit 0
}
#_____________________________________________________________________________________________
######################################## GUI ########################################
printf "\033c"
echo -e "This function automates IIS7 certificate generation for "$Company $UrOrg""
# Set Hostname and IP address
Hostname="$1"; [[ "$Hostname" =~ ^[-A-Za-z0-9]+$ ]]
while [ $? != 0 ]; do
read -p "Specify the server hostname (Not FQDN !): " Hostname
[[ "$Hostname" =~ ^[-A-Za-z0-9]+$ ]]
if [ $? != 0 ]; then echo "This hostname syntax is incorrect, try again !"; fi
[[ "$Hostname" =~ ^[-A-Za-z0-9]+$ ]]
done
LOGS=""$Hostname".logs"
# Set destination folder for SSL material
SaveIn+="/"$Hostname"";
if [ -d "$SaveIn" ]; then
echo "A folder named "$Hostname" already exists, Start over (delete existing materials) or quit ?"
echo -e "Select your choice and press [ENTER]\n\t[1] Start Over (Delete existing content)\n\t[2] Resume (Certificate Generation)\n\t[*] Quit"
read -p "Option number : " n
case $n in
1) rm -R "$SaveIn" > /dev/null 2>&1; mkdir -p "$SaveIn" > /dev/null 2>&1; cd "$SaveIn" ;;
2) cd "$SaveIn"; getcert "$#" ;;
*) echo "Aborting..."; exit 0 ;;
esac
else mkdir -p "$SaveIn"; cd "$SaveIn"
fi
###LOGGING GUI###
echo "`date "+%Y-%m-%d %H:%M:%S"` - Starting... Path: `pwd`" > $LOGS
echo "`date "+%Y-%m-%d %H:%M:%S"` - OpenSSL Version: `openssl version`" >> $LOGS
echo "`date "+%Y-%m-%d %H:%M:%S"` - cURL Version: `head -n 1 <(curl --version)`" >> $LOGS
echo "`date "+%Y-%m-%d %H:%M:%S"` - Server name: "$Hostname" FQDN: "$Hostname"."$Domain"" >> $LOGS
######################### 1- Get CSR ###############################
echo -e "\n\n`date "+%Y-%m-%d %H:%M:%S"` - Step 1: Code Signing Request" >> $LOGS
printf "\033c"
echo -e "This function automates IIS7 certificate generation for "$Company $UrOrg"
\tServer name:\t"$Hostname"\n\tFQDN:\t\t"$Hostname"."$Domain"\n"
echo -e "\nCode Signing Request (CSR):\n\tYou can generate a CSR and Private key or import a CSR (generated by an appliance and downloaded by you).
\tPlease note that importing a CSR means that the private key remains on the appliance or vendor's site.
\tSelect your choice and press [ENTER]\n\t[1] Generate CSR and Private Key\n\t[2] Import CSR\n\t[*] Quit"
read -p "Option number : " n
case $n in
1) gencsr "$#" ;;
2) importcsr "$#" ;;
*) echo "`date "+%Y-%m-%d %H:%M:%S"` - User Option: Quit" >> $LOGS; echo "Aborting..."; exit 0 ;;
esac
######################### 2- Submit CSR ############################
echo -e "\n\n`date "+%Y-%m-%d %H:%M:%S"` - Step 2: Submitting CSR" >> $LOGS
# Capture Attempt: Session ID cookie
echo "`date "+%Y-%m-%d %H:%M:%S"` - Capturing Session ID cookie from "$MSCA"" >> $LOGS
echo 'Capturing ASP Session ID (Cookie)...'
if [ -z "$Password" ]; then echo "What is the password for $Username ?: "; read -s Password; fi
RE=': ([^;]*);' #Regex to capture ASP Session ID from cookie string
while read l; do [[ $l =~ $RE ]] && AspSession="${BASH_REMATCH[1]}"; done <<<"$(grep "Cookie" <<< "$(curl --silent -Iku "$Username":"$Password" --ntlm https://"$MSCA"/certsrv/certrqxt.asp)")"
# If fail capturing cookie ==> Manual (Browser-Mode)
if [ -z "$AspSession" ]; then
echo "`date "+%Y-%m-%d %H:%M:%S"` - ERROR: Cannot capture Session ID cookie, failover to browser-mode..." >> $LOGS
echo "WARNING: Cannot capture Session ID cookie for "$MSCA", failover to browser-mode...\nPlease verify your credentials to connect to $MSCA\n\n"
echo "Paste CSR directly in internal CA web interface"
echo -e "\tConfirm the Subject Alternate Name field before submission !\n\tNote that the CSR may already include SAN(s) !
Current Subject: `openssl req -in *.csr -noout -text | grep "Subject:"`
Current SAN: `openssl req -in *.csr -noout -text | grep "DNS:"`"
clip <<< "$(cat *.csr 2> /dev/null)" ; Manual=1
echo -e "Please upload your Code Signing Request to your Internal Certificate Authority ("$MSCA") :"
if [ -z ${iexplore+x} ]; then
echo "Open "https://"$MSCA"/certsrv/certrqxt.asp" in a browser"
else
eval $iexplore "https://"$MSCA"/certsrv/certrqxt.asp" &
echo "Press any key to continue..." ; read
fi
fi
# If Session ID cookie sucessfully captured ==> Automatic (cURL-Mode)
if [ -z ${Manual+x} ]; then
echo "ASP cookie captured !"
# Set additional SAN (for cURL)
echo -e "\n\nConfirm the Subject Alternate Name before submission:
Current Common Name: `openssl req -in *.csr -noout -text | grep "Subject:"`
Current SAN: `openssl req -in *.csr -noout -text | grep "DNS:"`"
echo -e "\nDo you want to add a Subject Alternate Name (No) ?\nSelect your choice and press [ENTER]\n\t[1] Yes\n\t[*] No\n"
read -p "Option number : " n
case $n in
1) unset n; n=1 #Enter Loop
unset SAN; SAN="san%3Adns%3D"$Hostname"%26dns%3D"$Hostname.$Domain""
while (( n > 0 && n < 4 )); do
echo -e "\n\n\nDo you want to set an additional Subject Alternate Name ? (No)"
echo -e "Current SAN (URL Encoded): "$SAN""
echo -e "Select your choice and press [ENTER]\n\t[1] Add an IP address\n\t[2] Add an hostname\n\t[3] Reset SAN to default\n\t[*] Continue"
read -p "Option number : " n
case $n in
1) # Add Extra IP for SAN
while [[ -z ${IP+x} || $? != 0 ]]; do
read -p "What is the server's IP address: " IP
[[ "$IP" =~ ^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$ ]]
if [ $? != 0 ]; then echo "This IP address ("$IP") does not look quite right! Please try again..."; fi
[[ "$IP" =~ ^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$ ]]
done
SAN+="%26dns%3D"$IP""; unset IP
;;
2) # Add extra DNS name to SAN
while [[ -z ${extraSAN+x} || $? != 0 ]]; do
read -p "Specify a Fully Qualified Domain Name (FQDN) for the extra SAN: " extraSAN
[[ "$extraSAN" =~ ^[A-Za-z0-9.-]+$ ]]
if [ $? != 0 ]; then echo "This syntax is incorrect! Please try again..."; fi
[[ "$extraSAN" =~ ^[A-Za-z0-9.-]+$ ]]
done
SAN+="%26dns%3D"$extraSAN""; unset extraSAN
;;
3) SAN="san%3Adns%3D"$Hostname"%26dns%3D"$Hostname.$Domain"" ;;
*) n=4 ; SAN+='%0D%0A' ;; #Quit loop
esac
done
;;
*) ;;
esac
CertFormat=$(sed 's| |+|g' <<< $(sed 's|+|%2B|g' <<< $(sed 's|/|%2F|g' <<< $(sed ':a;N;$!ba;s/\n/%0D%0A/g' *.csr))))
Date=$(sed 's|%20|+|g' <<< $(urlencode "`date '+%m/%d/%Y,%r'`"))
cURLData="Mode=newreq&CertRequest="$CertFormat"&CertAttrib="$SAN"CertificateTemplate%3A"$CertTplt"%0D%0AUserAgent="$UA"%0D%0A&FriendlyType=Saved-Request+Certificate+%28"$Date"%29&ThumbPrint=&TargetStoreFlags=0&SaveCert=yes"
echo "`date "+%Y-%m-%d %H:%M:%S"` - Generating and encoding cURL POST data..." >> $LOGS
echo -e "Injecting crafted POST request to Internal CA using cURL and NTLM authentication...\n"
echo "`date "+%Y-%m-%d %H:%M:%S"` - Injecting crafted POST request to Internal CA using cURL and NTLM authentication..." >> $LOGS
InjectCmd="curl --silent -i -ku '$Username':'$Password' --ntlm '"https://"$MSCA"/certsrv/certfnsh.asp"' \
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \
-H 'Accept-Encoding: gzip, deflate' \
-H 'Accept-Language: en-US,en;q=0.5' \
-H 'Connection: keep-alive' -H 'Cookie: "$AspSession"' \
-H 'Host: "$MSCA"' \
-H 'Referer: https://"$MSCA"/certsrv/certrqxt.asp' \
-H 'User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko' \
-H 'Content-Type: application/x-www-form-urlencoded' --data '"$cURLData"'"
InjectCmdLog=`echo $InjectCmd | sed "s|"$Password"|<password>|g"`
echo "`date "+%Y-%m-%d %H:%M:%S"` - Command: "$InjectCmdLog"" >> $LOGS
echo "`date "+%Y-%m-%d %H:%M:%S"` - BEGIN HTTP REPLY: Consult "$Hostname".html" >> $LOGS
eval "$InjectCmd" &> "$Hostname.html"
echo "`date "+%Y-%m-%d %H:%M:%S"` - END HTTP REPLY" >> $LOGS
if [ $? != 0 ] || grep -q 'Access Denied' "$Hostname.html" || grep -q 'Denied by Policy Module' "$Hostname.html"; then
echo -e "Injection seems to have gone wrong! Please verify if the request is missing in the Certificate Authority Snap-In on "$MSCA""
echo -e "Consult Log file for analysis of the cURL query: it might be malformed!"
echo -e "Log file location: `pwd`/"$LOGS""
echo "`date "+%Y-%m-%d %H:%M:%S"` - Injection has failed !" >> $LOGS
exit 1
fi
fi
email "$to" -cc "$cc" -bcc "$bcc" -a "$Hostname".html -s "Certificate Request: Please issue $Hostname.$Domain certificate" -r $mailserver -p $mailport <<< "$MailTemplate"
echo "An email has been sent to the US (You are in CC) ! Once approved, please connect to "$MSCA" to retrieve your certificate using the Certificate Authority via mmc.exe"
echo "Once retrieved, open again this utility, enter the same hostname ("$Hostname") and resume operations: this will generates cryptographic material bundles (PEM, #PKCS12... etc.)"
echo "Please take notes of password you set to access the private key on the PKCS12 material"
I solved it in Java. this is the class:
package com.qequipe.dsiglib.p10;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.util.Base64;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.security.auth.x500.X500Principal;
import org.spongycastle.operator.ContentSigner;
import org.spongycastle.operator.OperatorCreationException;
import org.spongycastle.operator.jcajce.JcaContentSignerBuilder;
import org.spongycastle.pkcs.PKCS10CertificationRequest;
import org.spongycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.spongycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import com.qequipe.dsiglib.util.FileUtil;
public class P10Generator {
public static byte[] generate(KeyPair pair) throws OperatorCreationException, IOException
{
//KeyPair pair = generateKeyPair();
PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(
new X500Principal("CN=Requested Test Certificate"), pair.getPublic());
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA");
ContentSigner signer = csBuilder.build(pair.getPrivate());
PKCS10CertificationRequest csr = p10Builder.build(signer);
return csr.getEncoded();
}
public static void main(String[] args)
{
try
{
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(2048);
KeyPair pair = gen.generateKeyPair();
byte[] pkcs10 = generate(pair);
String base64pkcs10 = new String(Base64.getEncoder().encode(pkcs10));
System.out.println(new BigInteger(1, pkcs10).toString(16));
System.out.println(base64pkcs10);
Date date = new Date();
Authenticator.setDefault(new Authenticator() {
#Override
public PasswordAuthentication getPasswordAuthentication() {
System.out.println("Scheme:" + getRequestingScheme());
return new PasswordAuthentication("\\<username>", "password".toCharArray());
}
});
String request = "Mode=newreq&CertRequest=" + URLEncoder.encode(base64pkcs10) +
"&CertAttrib=" + URLEncoder.encode("CertificateTemplate:User\r\nUserAgent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36\r\n") +
"&FriendlyType=" + URLEncoder.encode("Saved-Request Certificate (" + (date.getDay()) + "/" + (date.getMonth() + 1) + "/" + date.getYear() + ", " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + ")") +
"&ThumbPrint=&TargetStoreFlags=0&SaveCert=yes";
byte[] cert = sendRequest("http://<yourcaurl>/certsrv/certfnsh.asp", request, "username", "password");
System.out.println(new BigInteger(1, cert).toString(16));
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
public static byte[] sendRequest(String urlString, String content, String username, String password) throws IOException {
HttpURLConnection con;
ByteArrayOutputStream out = new ByteArrayOutputStream();
URL url = new URL(urlString);
con = (HttpURLConnection) url.openConnection();
//String protocol = url.getProtocol();
con.setInstanceFollowRedirects(false);
con.setRequestMethod("POST");
//String encoded = Base64.getEncoder().encodeToString((username+":"+password).getBytes(StandardCharsets.UTF_8)); //Java 8
//con.setRequestProperty("Authorization", "NTML "+encoded);
con.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
con.setRequestProperty("Accept-Encoding","gzip, deflate");
con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
con.setRequestProperty("Connection","keep-alive");
con.setRequestProperty("Host", url.getHost());
con.setRequestProperty("Referer", url.getHost() + "/certsrv/certrqxt.asp");
con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko");
con.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
con.setRequestProperty("Content-Length", "" + content.length());
con.setDoOutput(true);
OutputStream outs = con.getOutputStream();
outs.write(content.getBytes());
outs.flush();
BufferedInputStream bis = new BufferedInputStream(con.getInputStream());
int length;
while ((length = bis.read()) != -1) {
out.write(length);
}
out.close();
System.out.println(out);
int respCode;
System.out.println("RESP code = " + (respCode = con.getResponseCode()));
//System.out.println("RESPONSE = \n" + out);
if(con.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST)
{
con.disconnect();
Pattern p = Pattern.compile("certnew.cer\\?ReqID=(\\d+)&");//. represents single character.
Matcher m = p.matcher(new String(out.toByteArray(), Charset.forName("UTF-8")));
if(m.find())
{
String reqid = m.group(1);
System.out.println(reqid);
URL certurl = new URL(url.getProtocol() +"://" + url.getHost() + "/certsrv/certnew.cer?ReqID=" + reqid + "&Enc=bin");
InputStream ins = certurl.openStream();
ByteArrayOutputStream bouts = new ByteArrayOutputStream();
FileUtil.copy(ins, bouts);
ins.close();
return bouts.toByteArray();
}
else
{
return null;
}
}
else
{
con.disconnect();
return null;
}
}
}
This is a combination of bits I've borrowed from forum posts including this one, and some stuff I've done myself. It has some stuff to accommodate Aruba Instant APs and the certificate format it accepts. Specifically tailored for the needs in my lab.
The answers here are great, and #SkazochNik J nailed it providing the final piece (automating the csr submission and retrieving the cert). #SkazochNik if you happen to see this, the last thing I'm looking for is pulling the CA cert from the CA via curl/bash. If you happen to know the format there, that would be awesome...
What I built (so far) in case this is useful:
#!/usr/bin/env bash
MSCA='dc1.myDomain.com' # Internal Microsoft Certification Authority
Username=<domain\\user>
Password=<pass>
echo "-- Certificate csr and key generator --"
echo "-- all certificates use myDomain.com domain --"
echo " "
echo "Is this certificate for a wireless controller/vc [y/n]"
read _isWlan
echo "provide all hostnames to be used as the CN first then additional SAN values (only the hostname)"
read _host1 _host2 _host3 _host4 _host5
echo "provide all IP addresses desired as SAN values"
read _ip1 _ip2 _ip3 _ip4 _ip5
if [ ${_isWlan,,} == 'y' ] || [ ${_isWlan,,} == 'yes' ]; then _isWlan=true; fi
if [ -z "$_host2" ]; then _dns2=false; else _dns2=true; fi
if [ -z "$_host3" ]; then _dns3=false; else _dns3=true; fi
if [ -z "$_host4" ]; then _dns4=false; else _dns4=true; fi
if [ -z "$_host5" ]; then _dns5=false; else _dns5=true; fi
if [ -z "$_ip2" ]; then _ip2in=false; else _ip2in=true; fi
if [ -z "$_ip3" ]; then _ip3in=false; else _ip3in=true; fi
if [ -z "$_ip4" ]; then _ip4in=false; else _ip4in=true; fi
if [ -z "$_ip5" ]; then _ip5in=false; else _ip5in=true; fi
echo "[req]" > $_host1.cnf
echo "default_bits = 2048" >> $_host1.cnf
echo "prompt = no" >> $_host1.cnf
echo "default_md = sha256" >> $_host1.cnf
echo "req_extensions = req_ext" >> $_host1.cnf
echo "distinguished_name = dn" >> $_host1.cnf
echo " " >> $_host1.cnf
echo "[ dn ]" >> $_host1.cnf
echo "C=US" >> $_host1.cnf
echo "ST=Indiana" >> $_host1.cnf
echo "L=Noblesville" >> $_host1.cnf
echo "O=WadeLab" >> $_host1.cnf
echo "OU=Engineering" >> $_host1.cnf
echo "emailAddress=wade1#hpe.com" >> $_host1.cnf
if $_isWlan; then
echo "CN = securelogin.myDomain.com" >> $_host1.cnf
else
echo "CN = "$_host1".myDomain.com" >> $_host1.cnf
fi
echo " " >> $_host1.cnf
echo "[ req_ext ]" >> $_host1.cnf
echo "subjectAltName = #alt_names" >> $_host1.cnf
echo " " >> $_host1.cnf
echo "[ alt_names ]" >> $_host1.cnf
# it would be prettier to populate an array from the input and loop through them, but it would take me longer
# to refresh my knowledge for the bash way to do that then it did to copy paste all this
if $_isWlan; then
echo "DNS.1 = securelogin.myDomain.com" >> $_host1.cnf
echo "DNS.2 = "$_host1".myDomain.com" >> $_host1.cnf
echo "DNS.3 = "$_host1 >> $_host1.cnf
if $_dns2; then
echo "DNS.4 = "$_host2".myDomain.com" >> $_host1.cnf
fi
if $_dns3; then
echo "DNS.5 = "$_host3".myDomain.com" >> $_host1.cnf
fi
if $_dns4; then
echo "DNS.6 = "$_host4".myDomain.com" >> $_host1.cnf
fi
if $_dns5; then
echo "DNS.7 = "$_host5".myDomain.com" >> $_host1.cnf
fi
else
echo "DNS.1 = "$_host1".myDomain.com" >> $_host1.cnf
echo "DNS.2 = "$_host1 >> $_host1.cnf
if $_dns2; then
echo "DNS.3 = "$_host2".myDomain.com" >> $_host1.cnf
fi
if $_dns3; then
echo "DNS.4 = "$_host3".myDomain.com" >> $_host1.cnf
fi
if $_dns4; then
echo "DNS.5 = "$_host4".myDomain.com" >> $_host1.cnf
fi
if $_dns5; then
echo "DNS.6 = "$_host5".myDomain.com" >> $_host1.cnf
fi
fi
echo "IP.1 = "$_ip1 >> $_host1.cnf
if $_ip2in; then
echo "IP.2 = "$_ip2 >> $_host1.cnf
fi
if $_ip3in; then
echo "IP.3 = "$_ip3 >> $_host1.cnf
fi
if $_ip4in; then
echo "IP.4 = "$_ip4 >> $_host1.cnf
fi
if $_ip5in; then
echo "IP.5 = "$_ip5 >> $_host1.cnf
fi
echo
echo "1. Creating Certificate Request..."
openssl req -nodes -newkey rsa:2048 -keyout $_host1.key -out $_host1.csr -config $_host1.cnf
#echo "--- csr and key creation complete ---"
#echo " "
#echo "---------------------------"
#echo "-----Below is your CSR-----"
#echo "---------------------------"
#cat $_host1.csr
#echo
#echo
# >> Start Send csr to CA and retrieve certificate
CERT=`cat $_host1.csr | tr -d '\n\r'`
DATA="Mode=newreq&CertRequest=${CERT}&C&TargetStoreFlags=0&SaveCert=yes"
CERT=`echo ${CERT} | sed 's/+/%2B/g'`
CERT=`echo ${CERT} | tr -s ' ' '+'`
CERTATTRIB="CertificateTemplate:WebServerv2Exportable%0D%0A"
echo "2. Request cert from "$MSCA"..."
OUTPUTLINK=`curl -k -u "${Username}":${Password} --ntlm \
"https://${MSCA}/certsrv/certfnsh.asp" \
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \
-H 'Accept-Encoding: gzip, deflate' \
-H 'Accept-Language: en-US,en;q=0.5' \
-H 'Connection: keep-alive' \
-H "Host: ${MSCA}" \
-H "Referer: https://${MSCA}/certsrv/certrqxt.asp" \
-H 'User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data "Mode=newreq&CertRequest=${CERT}&CertAttrib=${CERTATTRIB}&TargetStoreFlags=0&SaveCert=yes&ThumbPrint=" | grep -A 1 'function handleGetCert() {' | tail -n 1 | cut -d '"' -f 2`
CERTLINK="https://${MSCA}/certsrv/${OUTPUTLINK}"
echo "3. Retrive cert: "$CERTLINK"..."
curl -k -u "${Username}":${Password} --ntlm $CERTLINK \
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \
-H 'Accept-Encoding: gzip, deflate' \
-H 'Accept-Language: en-US,en;q=0.5' \
-H 'Connection: keep-alive' \
-H "Host: ${MSCA}" \
-H "Referer: https://${MSCA}/certsrv/certrqxt.asp" \
-H 'User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko' \
-H 'Content-Type: application/x-www-form-urlencoded' > $_host1.cer
echo "4. Verifying cert for "$_host1"..."
openssl verify -CAfile ca.cer -verbose $_host1.cer
if [ "0" -eq "$?" ] ;
then
echo "Certificate Validation Completed Succesfully"
echo
#exit 0
else
echo "Error code: "$?". Stopping."
exit 1
fi
#/END send csr to CA and retrieve certificate
if $_isWlan; then
echo "Do you need the certficate combined with the CA and key for Aruba IAP? [y/n]"
read _isIap
fi
if [ ${_isIap,,} == 'y' ] || [ ${_isIap,,} == 'yes' ]; then
echo "-------------------------------------------------------"
echo "----- Combining Server CA and Key file for VC -----"
echo "-------------------------------------------------------"
echo
# echo "-----Please Paste in the resulting PEM certificate-----"
# echo "----- then press [CTRL+D] to continue -----"
# echo "-------------------------------------------------------"
# _svrCert=$(</dev/stdin)
# echo "$_svrCert" > tmp.cer
# no error check right now for existing of CA.cer
mv $_host1.cer tmp.cer
cat ca.cer >> tmp.cer
cat $_host1.key >> tmp.cer
cat tmp.cer | sed '/^[[:space:]]*$/d' > $_host1.cer ##Remove blank lines
rm -f tmp.cer
echo "--------------------------------------------------"
echo "-----Cert Creation Complete. Combined cert ------"
echo "----- is in this directory "$_host1".cer ------"
echo "--------------------------------------------------"
else
echo "--------------------------------------------------"
echo "--------------------- DONE ---------------------"
echo "----- "$_host1".cer is in this directory ------"
echo "--------------------------------------------------"
fi
echo
echo Outputting Certificate Contents to Display...
sleep 4
clear
cat $_host1.cer
I have a handy script here that can return accounts that will expire in 7 Days or have expired. I wanted to allow this to run on multiple hosts with out putting the script on each individual host, I added the for loop and the ssh $SERVER >> EOF part but it will just run the commands off they system that is running the script.
I believe the error is with ssh $SERVER >> EOF but I am unsure as the syntax looks correct.
#!/bin/bash
for SERVER in `cat /lists/testlist`
do
echo $SERVER
ssh $SERVER >> EOF
sudo cat /etc/shadow | cut -d: -f1,8 | sed /:$/d > /tmp/expirelist.txt
totalaccounts=`sudo cat /tmp/expirelist.txt | wc -l`
for((i=1; i<=$totalaccounts; i++ ))
do
tuserval=`sudo head -n $i /tmp/expirelist.txt | tail -n 1`
username=`sudo echo $tuserval | cut -f1 -d:`
userexp=`sudo echo $tuserval | cut -f2 -d:`
userexpireinseconds=$(( $userexp * 86400 ))
todaystime=`date +"%s"`
if [[ $userexpireinseconds -ge $todaystime ]] ;
then
timeto7days=$(( $todaystime + 604800 ))
if [[ $userexpireinseconds -le $timeto7days ]];
then
echo $username "is going to expire in 7 Days"
fi
else
echo $username "account has expired"
fi
done
sudo rm /tmp/expirelist.txt
EOF
done
Here documents are started by << EOF (or, better, << 'EOF' to prevent the body of the here document being expanded by the (local) shell) and the end marker must be in column 1.
What you're doing is running ssh and appending standard output to a file EOF (>> is an output redirection; << is an input redirection). It is then (locally) running sudo, etc. It probably fails to execute the local file EOF (not executable, one hopes), and likely doesn't find any other command for that either.
I think what you're after is this (where I've now replaced the back-ticks in the script with $(...) notation, and marginally optimized the server list generation for use with Bash):
#!/bin/bash
for SERVER in $(</lists/testlist)
do
echo $SERVER
ssh $SERVER << 'EOF'
sudo cat /etc/shadow | cut -d: -f1,8 | sed '/:$/d' > /tmp/expirelist.txt
totalaccounts=$(sudo cat /tmp/expirelist.txt | wc -l)
for ((i=1; i<=$totalaccounts; i++))
do
tuserval=$(sudo head -n $i /tmp/expirelist.txt | tail -n 1)
username=$(sudo echo $tuserval | cut -f1 -d:)
userexp=$(sudo echo $tuserval | cut -f2 -d:)
userexpireinseconds=$(( $userexp * 86400 ))
todaystime=$(date +"%s")
if [[ $userexpireinseconds -ge $todaystime ]]
then
timeto7days=$(( $todaystime + 604800 ))
if [[ $userexpireinseconds -le $timeto7days ]]
then
echo $username "is going to expire in 7 Days"
fi
else
echo $username "account has expired"
fi
done
sudo rm /tmp/expirelist.txt
EOF
done
Very close, but the differences really matter! Note, in particular, that the end marker EOF is in column 1 and not indented at all.
If I have the actual file and a Bash shell in Mac or Linux, how can I query the cert file for when it will expire? Not a web site, but actually the certificate file itself, assuming I have the csr, key, pem and chain files.
With openssl:
openssl x509 -enddate -noout -in file.pem
The output is on the form:
notAfter=Nov 3 22:23:50 2014 GMT
Also see MikeW's answer for how to easily check whether the certificate has expired or not, or whether it will within a certain time period, without having to parse the date above.
If you just want to know whether the certificate has expired (or will do so within the next N seconds), the -checkend <seconds> option to openssl x509 will tell you:
if openssl x509 -checkend 86400 -noout -in file.pem
then
echo "Certificate is good for another day!"
else
echo "Certificate has expired or will do so within 24 hours!"
echo "(or is invalid/not found)"
fi
This saves having to do date/time comparisons yourself.
openssl will return an exit code of 0 (zero) if the certificate has not expired and will not do so for the next 86400 seconds, in the example above. If the certificate will have expired or has already done so - or some other error like an invalid/nonexistent file - the return code is 1.
(Of course, it assumes the time/date is set correctly)
Be aware that older versions of openssl have a bug which means if the time specified in checkend is too large, 0 will always be returned (https://github.com/openssl/openssl/issues/6180).
Here's my bash command line to list multiple certificates in order of their expiration, most recently expiring first.
for pem in /etc/ssl/certs/*.pem; do
printf '%s: %s\n' \
"$(date --date="$(openssl x509 -enddate -noout -in "$pem"|cut -d= -f 2)" --iso-8601)" \
"$pem"
done | sort
Sample output:
2015-12-16: /etc/ssl/certs/Staat_der_Nederlanden_Root_CA.pem
2016-03-22: /etc/ssl/certs/CA_Disig.pem
2016-08-14: /etc/ssl/certs/EBG_Elektronik_Sertifika_Hizmet_S.pem
Command:
# cat {key_name} | openssl x509 -noout -enddate
Example: # cat tower.cert | openssl x509 -noout -enddate
Result:
notAfter=Dec 7 04:03:32 2023 GMT
Here's a bash function which checks all your servers, assuming you're using DNS round-robin. Note that this requires GNU date and won't work on Mac OS
function check_certs () {
if [ -z "$1" ]
then
echo "domain name missing"
exit 1
fi
name="$1"
shift
now_epoch=$( date +%s )
dig +noall +answer $name | while read _ _ _ _ ip;
do
echo -n "$ip:"
expiry_date=$( echo | openssl s_client -showcerts -servername $name -connect $ip:443 2>/dev/null | openssl x509 -inform pem -noout -enddate | cut -d "=" -f 2 )
echo -n " $expiry_date";
expiry_epoch=$( date -d "$expiry_date" +%s )
expiry_days="$(( ($expiry_epoch - $now_epoch) / (3600 * 24) ))"
echo " $expiry_days days"
done
}
Output example:
$ check_certs stackoverflow.com
151.101.1.69: Aug 14 12:00:00 2019 GMT 603 days
151.101.65.69: Aug 14 12:00:00 2019 GMT 603 days
151.101.129.69: Aug 14 12:00:00 2019 GMT 603 days
151.101.193.69: Aug 14 12:00:00 2019 GMT 603 days
Same as accepted answer, But note that it works even with .crt file and not just .pem file, just in case if you are not able to find .pem file location.
openssl x509 -enddate -noout -in e71c8ea7fa97ad6c.crt
Result:
notAfter=Mar 29 06:15:00 2020 GMT
One line checking on true/false if cert of domain will be expired in some time later(ex. 15 days):
openssl x509 -checkend $(( 24*3600*15 )) -noout -in <(openssl s_client -showcerts -connect my.domain.com:443 </dev/null 2>/dev/null | openssl x509 -outform PEM)
if [ $? -eq 0 ]; then
echo 'good'
else
echo 'bad'
fi
For MAC OSX (El Capitan) This modification of Nicholas' example worked for me.
for pem in /path/to/certs/*.pem; do
printf '%s: %s\n' \
"$(date -jf "%b %e %H:%M:%S %Y %Z" "$(openssl x509 -enddate -noout -in "$pem"|cut -d= -f 2)" +"%Y-%m-%d")" \
"$pem";
done | sort
Sample Output:
2014-12-19: /path/to/certs/MDM_Certificate.pem
2015-11-13: /path/to/certs/MDM_AirWatch_Certificate.pem
macOS didn't like the --date= or --iso-8601 flags on my system.
If (for some reason) you want to use a GUI application in Linux, use gcr-viewer (in most distributions it is installed by the package gcr (otherwise in package gcr-viewer))
gcr-viewer file.pem
# or
gcr-viewer file.crt
Into bash variables
As this question is tagged bash, I often use UNIX EPOCH to store dates, this is useful for compute time left with $EPOCHSECONDS and format output via printf '%(dateFmt)T bashism:
{ read -r certStart;read -r certEnd;}< <(date -f <(cut -d = -f 2 <(
openssl x509 -dates -noout -in "$file")) +%s)
Then
printf '%-6s %(%a %d %b %Y, %H %Z)T\n' start $certStart end $certEnd
start Mon 01 Nov 2004, 17 UTC
end Mon 01 Jan 2035, 05 UTC
Sample, listing content of /etc/ssl/certs and compute days left:
for file in /etc/ssl/certs/*pem;do
{ read -r certStart;read -r certEnd;}< <(
date -f <(cut -d = -f 2 <(
openssl x509 -dates -noout -in "$file")) +%s)
printf "%(%d %b %Y %T)T - %(%d %b %Y %T)T: %6d %s\n" \
$certStart $certEnd $(( (certEnd - EPOCHSECONDS)/86400 )) ${file##*/}
done
05 May 2011 09:37:37 - 31 Dec 2030 09:37:37: 3034 ACCVRAIZ1.pem
26 Oct 2010 08:38:03 - 26 Oct 2040 08:38:03: 6620 Buypass_Class_2_Root_CA.pem
19 Jan 2010 00:00:00 - 18 Jan 2038 23:59:59: 5609 COMODO_RSA_Certification_Authority.pem
13 Nov 2012 00:00:00 - 19 Jan 2038 03:14:07: 5609 GlobalSign_ECC_Root_CA_-_R4.pem
06 Apr 2001 07:29:40 - 06 Apr 2021 07:29:40: -522 Sonera_Class_2_Root_CA.pem
29 Jun 2004 17:39:16 - 29 Jun 2034 17:39:16: 4310 Starfield_Class_2_CA.pem
04 Feb 2016 12:32:16 - 31 Dec 2029 17:23:16: 2669 TrustCor_RootCert_CA-1.pem
01 Nov 2004 17:14:04 - 01 Jan 2035 05:37:19: 4495 XRamp_Global_CA_Root.pem
...
More complete bash x509 reading:
for file in /etc/ssl/certs/*pem;do
mapfile -t x509 < <(openssl x509 -noout -dates -subject -in "$file")
x509=("${x509[#]#*=}")
mapfile -t dates < <(IFS=$'\n';date -f - <<<"${x509[*]::2}" +%s)
str="${x509[-1]}"
declare -A Subj='([CN]="${file##*/}")'
while [[ "$str" ]] ;do
lhs=${str%%=*} rhs=${str#$lhs= } rhs=${rhs%% = *} rhs=${rhs%, *}
Subj[${lhs// }]="$rhs"
str=${str#"$lhs= $rhs"} str=${str#, }
done
printf "%(%d %b %Y %T)T - %(%d %b %Y %T)T: %s\n" \
${dates[#]} "${Subj[CN]}"
done
05 May 2011 09:37:37 - 31 Dec 2030 09:37:37: 3034 ACCVRAIZ1
26 Oct 2010 08:38:03 - 26 Oct 2040 08:38:03: 6620 Buypass Class 2 Root CA
19 Jan 2010 00:00:00 - 18 Jan 2038 23:59:59: 5609 COMODO RSA Certification Authority
13 Nov 2012 00:00:00 - 19 Jan 2038 03:14:07: 5609 GlobalSign
06 Apr 2001 07:29:40 - 06 Apr 2021 07:29:40: -522 Sonera Class2 CA
29 Jun 2004 17:39:16 - 29 Jun 2034 17:39:16: 4310 Starfield_Class_2_CA.pem
04 Feb 2016 12:32:16 - 31 Dec 2029 17:23:16: 2669 TrustCor RootCert CA-1
01 Nov 2004 17:14:04 - 01 Jan 2035 05:37:19: 4495 XRamp Global Certification Authority
...
Note: Some certs don't have CN field in subject. For this I've initialized $Subj array by setting CN field to filename:
declare -A Subj='([CN]="${file##*/}")'
Full bash script
Sharing here a full bash script, showing all certificates from command line arguments, which could by file, domain name or IPv4 address. Will ouput past days, days left, number of alternative domain, and all alts in one (long) line:
#!/bin/bash
showCert() {
local x509 dates lhs rhs str alts
mapfile -t x509 < <(
openssl x509 -noout -dates -subject -ext subjectAltName -in "$1")
x509=("${x509[#]#*=}")
mapfile -t dates < <(IFS=$'\n';date -f - <<<"${x509[*]::2}" +%s)
str="${x509[2]}"
local -A Subj;Subj[CN]="${file##*/}"
while [[ -n "$str" ]]; do
lhs=${str%%=*} rhs=${str#$lhs= } rhs=${rhs%% = *} rhs=${rhs%, *}
Subj[${lhs// }]="$rhs"
str=${str#"$lhs= $rhs"} str=${str#, }
done
read -ra alts <<<"${x509[4]//,}"
alts=("${alts[#]#*:}")
printf " %(%d %b %Y %H:%M)T %(%d %b %Y %H:%M)T %6d %6d %-30s %3d %s\n" \
"${dates[#]}" $(((dates[1]-EPOCHSECONDS)/86400)) $(((EPOCHSECONDS-
dates[0])/86400)) "${Subj[CN]}" "${#alts[#]}" "${alts[*]}"
}
checkIsIpv4() { # throw an error if not valid IPv4
local _iPointer _i _a _vareq=()
for _i ;do
case $_i in *[^0-9.]* ) return 1 ;; esac
read -ra _a <<<"${_i//./ }"
[ ${#_a[#]} -eq 4 ] || return 1
for _iPointer in "${_a[#]}" ;do
(( _iPointer == ( _iPointer & 255 ) )) || return 2
done
done
}
checkIsLabel() {
((${#1}<4 || ${#1}>253)) && return 1
[[ -z ${1//[a-zA-Z0-9.-]} ]] || return 2
[[ -z ${1//.} ]] && return 3
set -- ${1//./ }
(($#<2 )) && return 4
:
}
printf ' %-17s %-17s %6s %6s %-30s %2s\n' Not\ before Not\ after left \
past Common\ Name Alt
for arg ;do
if [ -f "$arg" ] ;then
showCert "$arg"
elif checkIsLabel "$arg" || checkIsIpv4 "$arg" ;then
showCert <(openssl s_client -ign_eof -connect "$arg:443" \
<<<$'HEAD / HTTP/1.0\r\n\r' 2> /dev/null)
else
echo "Unknown argument: '$arg'."
fi
done
Usage sample:
./certShow.sh /etc/ssl/certs/ssl-cert-snakeoil.pem www.example.com
Not before Not after left past Common Name Alt
08 Sep 2021 16:49 06 Sep 2031 16:49 3277 372 hostname.local 1 hostname.local
14 Mar 2022 00:00 14 Mar 2023 23:59 179 186 www.example.org 8 www.example.org example.net example.edu example.com example.org www.example.com www.example.edu www.example.net
I have made a bash script related to the same to check if the certificate is expired or not. You can use the same if required.
Script
https://github.com/zeeshanjamal16/usefulScripts/blob/master/sslCertificateExpireCheck.sh
ReadMe
https://github.com/zeeshanjamal16/usefulScripts/blob/master/README.md