将 Base64 CSR 提交给 Microsoft CA(通过 cURL)

Submitting Base64 CSR to a Microsoft CA (via cURL)

我已经编写了一个 bash 脚本来根据此 ServerFault link.

自动生成 IIS7 证书

我想通过 cURL 自动将代码签名请求 (CSR) 发送到内部 Microsoft 证书颁发机构 (MS CA),以下代码很有前途并已成功将 CSR 提交给 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)"

我有兴趣在修改后重播此请求:

  1. 解码--数据(确定)
  2. 修改--数据(确定)
  3. 重新编码...(不正确)

编码:

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

解码:

 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

重新编码:(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

上面链接的三个网站(重新编码)都无法正确重新编码。 棘手的部分是“=”和“&”不应该被编码。

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

我可以具体地使用sed这个例子,但我想知道是否有办法知道服务器期望的编码,并编码在正确的字符集中 自动 。这可能吗?

这是 Windows (CYGWIN) 上的 bash 脚本:

#!/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" ""; 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 "3c"
    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 "" "")"
    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 "3c"
    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 "3c"
echo -e "This function automates IIS7 certificate generation for "$Company $UrOrg""

# Set Hostname and IP address
Hostname=""; [[ "$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 "3c"
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"
   

我在 bash 上解决了 linux 和 curl:

#!/bin/sh

# tested on SUSE Linux 12 SP1

#  - CN Object name
#  - username
#  - password

MSCA='HOSTNAME'  # Internal Microsoft Certification Authority
Username=
Password=

function show_usage()
{
    echo "Scrip for retrive certificate from MS SubCA"
    echo "Usage: [=10=] <CN> [domain\\username] [password]"
    echo " "
    echo "Example: [=10=] example.com workgroup\\foo bar"
    exit 0
}

if [ -z "" ]
then
    show_usage
    exit 0
fi

if [ -z "" ]
then
    Username="workgroup\foo"
    Password="bar"
fi


echo -e "\e[32m1. Generate private key...\e[0m"
openssl req -new -nodes -out .pem -keyout .key -subj "/C=RU/ST=State/L=City/O=Org/CN=/emailAddress=postmaster@example.com"
CERT=`cat .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' > .crt

echo -e "\e[32m4. Verifying cert for \e[0m"
openssl verify -verbose .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

这是我从论坛帖子(包括这篇帖子)中借用的部分以及我自己完成的一些内容的组合。它有一些东西可以容纳 Aruba Instant AP 和它接受的证书格式。专为满足我实验室的需求而量身定制。

这里的答案很好,@SkazochNik J 提供了最后一块(自动提交 csr 并检索证书)。 @SkazochNik 如果你碰巧看到这个,我要找的最后一件事就是通过 curl/bash 从 CA 中提取 CA 证书。如果你碰巧知道那里的格式,那就太棒了......

如果这有用,我构建的(到目前为止):

#!/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

我在Java解决了。这是 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;
        }
    }
}