6

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:

  1. Decode --data (OK)
  2. Modify --data (OK)
  3. 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 ?

Community
  • 1
  • 1
Florian Bidabé
  • 640
  • 8
  • 22
  • Hi Florian, i am planning to do something similar. Have you managed to put it to work? – Rui F Ribeiro Jan 13 '16 at 15:19
  • Hello Rui, I did yes, the reason it is not posted yet is because the script isn't finished however sending CSR via cURL is working without a glitch. I'll post it swap – Florian Bidabé Jan 13 '16 at 23:04
  • Thanks for the attention, will read it more attentively later on. – Rui F Ribeiro Feb 02 '16 at 15:58
  • 1
    Rui, if you want to jump straight to cURL injection, have a look at "InjectCmd" in the script. It has many variations as the request includes NTLM authentication, a crafted date, an optional crafted SAN (additional attribute); a cookie session ID from MSCA IIS server and an optional user agent (static in my case=firefox) – Florian Bidabé Feb 05 '16 at 00:34

4 Answers4

10

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
SkazochNik J
  • 116
  • 2
  • 4
  • Thank you so much! Note that I had to use "domain\username" to avoid the "No mapping between account names and security IDs was done" error. Also your headers were not required for me. – pedroapero Dec 06 '19 at 17:31
7

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"
   
Florian Bidabé
  • 640
  • 8
  • 22
1

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;
        }
    }
}
Ugo Chirico
  • 347
  • 3
  • 6
0

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
Wade
  • 45
  • 5
  • Thanks mate, I need to update my post as I've considerably improved my script. It now's allow to resume operation in the scenario that you're waiting on a security administrator to issue your certificate. Also improve logging and send an email notification. – Florian Bidabé Nov 11 '17 at 05:50