Support RSA and ECDSA dual certs (#262)

* Support RSA and ECDSA dual certs

* minor

* fix RSA and ECC dual certs

* minor
This commit is contained in:
neil 2016-08-13 19:22:25 +08:00 committed by GitHub
parent 527dd31c70
commit 43822d37a7
1 changed files with 207 additions and 89 deletions

296
acme.sh
View File

@ -1,6 +1,6 @@
#!/usr/bin/env sh
VER=2.3.6
VER=2.4.0
PROJECT_NAME="acme.sh"
@ -36,6 +36,9 @@ END_CERT="-----END CERTIFICATE-----"
RENEW_SKIP=2
ECC_SEP="_"
ECC_SUFFIX="${ECC_SEP}ecc"
if [ -z "$AGREEMENT" ] ; then
AGREEMENT="$DEFAULT_AGREEMENT"
fi
@ -47,25 +50,62 @@ if [ "$(printf '\x41')" != 'A' ] ; then
_URGLY_PRINTF=1
fi
__green() {
printf '\033[1;31;32m'
printf -- "$1"
printf '\033[0m'
}
__red() {
printf '\033[1;31;40m'
printf -- "$1"
printf '\033[0m'
}
_info() {
if [ -z "$2" ] ; then
echo "[$(date)] $1"
printf -- "[$(date)] $1"
else
echo "[$(date)] $1='$2'"
printf -- "[$(date)] $1='$2'"
fi
printf "\n"
}
_err_e() {
if [ -z "$2" ] ; then
__red "$1" >&2
else
__red "$1='$2'" >&2
fi
}
_err() {
_info "$@" >&2
printf -- "[$(date)] " >&2
_err_e "$@"
printf "\n"
return 1
}
_usage() {
version
_err_e "$@"
printf "\n"
}
_debug() {
if [ -z "$DEBUG" ] ; then
return
fi
_err "$@"
if [ -z "$2" ] ; then
printf -- "[$(date)] $1" >&2
else
printf -- "[$(date)] $1='$2'" >&2
fi
printf "\n"
return 0
}
@ -89,10 +129,16 @@ _startswith(){
echo "$_str" | grep "^$_sub" >/dev/null 2>&1
}
_endswith(){
_str="$1"
_sub="$2"
echo "$_str" | grep -- "$_sub\$" >/dev/null 2>&1
}
_contains(){
_str="$1"
_sub="$2"
echo "$_str" | grep "$_sub" >/dev/null 2>&1
echo "$_str" | grep -- "$_sub" >/dev/null 2>&1
}
_hasfield() {
@ -100,7 +146,7 @@ _hasfield() {
_field="$2"
_sep="$3"
if [ -z "$_field" ] ; then
_err "Usage: str field [sep]"
_usage "Usage: str field [sep]"
return 1
fi
@ -121,7 +167,7 @@ _hasfield() {
_exists(){
cmd="$1"
if [ -z "$cmd" ] ; then
_err "Usage: _exists cmd"
_usage "Usage: _exists cmd"
return 1
fi
if type command >/dev/null 2>&1 ; then
@ -208,7 +254,7 @@ _sed_i() {
options="$1"
filename="$2"
if [ -z "$filename" ] ; then
_err "Usage:_sed_i options filename"
_usage "Usage:_sed_i options filename"
return 1
fi
_debug2 options "$options"
@ -236,7 +282,7 @@ _getfile() {
startline="$2"
endline="$3"
if [ -z "$endline" ] ; then
_err "Usage: file startline endline"
_usage "Usage: file startline endline"
return 1
fi
@ -283,7 +329,7 @@ _dbase64() {
_digest() {
alg="$1"
if [ -z "$alg" ] ; then
_err "Usage: _digest hashalg"
_usage "Usage: _digest hashalg"
return 1
fi
@ -308,7 +354,7 @@ _sign() {
keyfile="$1"
alg="$2"
if [ -z "$alg" ] ; then
_err "Usage: _sign keyfile hashalg"
_usage "Usage: _sign keyfile hashalg"
return 1
fi
@ -321,27 +367,29 @@ _sign() {
}
#keylength
_isEccKey() {
_length="$1"
if [ -z "$_length" ] ;then
return 1
fi
[ "$_length" != "1024" ] \
&& [ "$_length" != "2048" ] \
&& [ "$_length" != "3172" ] \
&& [ "$_length" != "4096" ] \
&& [ "$_length" != "8192" ]
}
# _createkey 2048|ec-256 file
_createkey() {
length="$1"
f="$2"
isec=""
eccname="$length"
if _startswith "$length" "ec-" ; then
isec="1"
length=$(printf $length | cut -d '-' -f 2-100)
eccname="$length"
fi
if [ -z "$length" ] ; then
if [ "$isec" ] ; then
length=256
else
length=2048
fi
fi
_info "Use length $length"
if [ "$isec" ] ; then
if [ "$length" = "256" ] ; then
eccname="prime256v1"
fi
@ -351,15 +399,27 @@ _createkey() {
if [ "$length" = "521" ] ; then
eccname="secp521r1"
fi
_info "Using ec name: $eccname"
fi
#generate account key
if [ "$isec" ] ; then
if [ -z "$length" ] ; then
length=2048
fi
_info "Use length $length"
if _isEccKey "$length" ; then
_info "Using ec name: $eccname"
openssl ecparam -name $eccname -genkey 2>/dev/null > "$f"
else
_info "Using RSA: $length"
openssl genrsa $length 2>/dev/null > "$f"
fi
if [ "$?" != "0" ] ; then
_err "Create key error."
return 1
fi
}
#_createcsr cn san_list keyfile csrfile conf
@ -385,7 +445,7 @@ _createcsr() {
fi
#multi
_info "Multi domain" "$alt"
printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\nreq_extensions = v3_req\n[ v3_req ]\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment\nsubjectAltName=$alt" > "$csrconf"
printf -- "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\nreq_extensions = v3_req\n[ v3_req ]\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment\nsubjectAltName=$alt" > "$csrconf"
openssl req -new -sha256 -key "$key" -subj "/CN=$domain" -config "$csrconf" -out "$csr"
fi
}
@ -434,16 +494,19 @@ _ss() {
return 1
}
#domain [password] [isEcc]
toPkcs() {
domain="$1"
pfxPassword="$2"
if [ -z "$domain" ] ; then
echo "Usage: $PROJECT_ENTRY --toPkcs -d domain [--password pfx-password]"
_usage "Usage: $PROJECT_ENTRY --toPkcs -d domain [--password pfx-password]"
return 1
fi
_initpath "$domain"
_isEcc="$3"
_initpath "$domain" "$_isEcc"
if [ "$pfxPassword" ] ; then
openssl pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" -password "pass:$pfxPassword"
else
@ -460,7 +523,7 @@ toPkcs() {
createAccountKey() {
_info "Creating account key"
if [ -z "$1" ] ; then
echo Usage: $PROJECT_ENTRY --createAccountKey -d domain.com [--accountkeylength 2048]
_usage "Usage: $PROJECT_ENTRY --createAccountKey -d domain.com [--accountkeylength 2048]"
return
fi
@ -488,18 +551,18 @@ createAccountKey() {
}
#domain length
#domain [length]
createDomainKey() {
_info "Creating domain key"
if [ -z "$1" ] ; then
echo Usage: $PROJECT_ENTRY --createDomainKey -d domain.com [ --keylength 2048 ]
_usage "Usage: $PROJECT_ENTRY --createDomainKey -d domain.com [ --keylength 2048 ]"
return
fi
domain=$1
_initpath $domain
length=$2
_initpath $domain "$length"
if [ ! -f "$CERT_KEY_PATH" ] || ( [ "$FORCE" ] && ! [ "$IS_RENEW" ] ); then
_createkey "$length" "$CERT_KEY_PATH"
@ -516,23 +579,30 @@ createDomainKey() {
}
# domain domainlist
# domain domainlist isEcc
createCSR() {
_info "Creating csr"
if [ -z "$1" ] ; then
echo "Usage: $PROJECT_ENTRY --createCSR -d domain1.com [-d domain2.com -d domain3.com ... ]"
_usage "Usage: $PROJECT_ENTRY --createCSR -d domain1.com [-d domain2.com -d domain3.com ... ]"
return
fi
domain=$1
_initpath "$domain"
domainlist=$2
domain="$1"
domainlist="$2"
_isEcc="$3"
_initpath "$domain" "$_isEcc"
if [ -f "$CSR_PATH" ] && [ "$IS_RENEW" ] && [ -z "$FORCE" ]; then
_info "CSR exists, skip"
return
fi
if [ ! -f "$CERT_KEY_PATH" ] ; then
_err "The key file is not found: $CERT_KEY_PATH"
_err "Please create the key file first."
return 1
fi
_createcsr "$domain" "$domainlist" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"
}
@ -582,7 +652,7 @@ _stat() {
_calcjwk() {
keyfile="$1"
if [ -z "$keyfile" ] ; then
_err "Usage: _calcjwk keyfile"
_usage "Usage: _calcjwk keyfile"
return 1
fi
EC_SIGN=""
@ -825,7 +895,7 @@ _setopt() {
__val="$4"
__end="$5"
if [ -z "$__opt" ] ; then
echo usage: _setopt '"file" "opt" "=" "value" [";"]'
_usage usage: _setopt '"file" "opt" "=" "value" [";"]'
return
fi
if [ ! -f "$__conf" ] ; then
@ -1012,6 +1082,7 @@ _starttlsserver() {
_debug serverproc $serverproc
}
#[domain] [keylength]
_initpath() {
if [ -z "$LE_WORKING_DIR" ] ; then
@ -1091,55 +1162,67 @@ _initpath() {
fi
domain="$1"
length="$2"
if [ -z "$domain" ] ; then
return 0
fi
domainhome="$CERT_HOME/$domain"
mkdir -p "$domainhome"
if [ -z "$DOMAIN_PATH" ] ; then
domainhome="$CERT_HOME/$domain"
domainhomeecc="$CERT_HOME/$domain$ECC_SUFFIX"
DOMAIN_PATH="$domainhome"
if _isEccKey "$length" ; then
DOMAIN_PATH="$domainhomeecc"
else
if [ ! -d "$domainhome" ] && [ -d "$domainhomeecc" ] ; then
_info "The domain '$domain' seems to be a ECC domain, please add '$(__red "--ecc")' parameter next time."
DOMAIN_PATH="$domainhomeecc"
fi
fi
_debug DOMAIN_PATH "$DOMAIN_PATH"
fi
if [ -z "$DOMAIN_CONF" ] ; then
DOMAIN_CONF="$domainhome/$domain.conf"
DOMAIN_CONF="$DOMAIN_PATH/$domain.conf"
fi
if [ -z "$DOMAIN_SSL_CONF" ] ; then
DOMAIN_SSL_CONF="$domainhome/$domain.ssl.conf"
DOMAIN_SSL_CONF="$DOMAIN_PATH/$domain.ssl.conf"
fi
if [ -z "$CSR_PATH" ] ; then
CSR_PATH="$domainhome/$domain.csr"
CSR_PATH="$DOMAIN_PATH/$domain.csr"
fi
if [ -z "$CERT_KEY_PATH" ] ; then
CERT_KEY_PATH="$domainhome/$domain.key"
CERT_KEY_PATH="$DOMAIN_PATH/$domain.key"
fi
if [ -z "$CERT_PATH" ] ; then
CERT_PATH="$domainhome/$domain.cer"
CERT_PATH="$DOMAIN_PATH/$domain.cer"
fi
if [ -z "$CA_CERT_PATH" ] ; then
CA_CERT_PATH="$domainhome/ca.cer"
CA_CERT_PATH="$DOMAIN_PATH/ca.cer"
fi
if [ -z "$CERT_FULLCHAIN_PATH" ] ; then
CERT_FULLCHAIN_PATH="$domainhome/fullchain.cer"
CERT_FULLCHAIN_PATH="$DOMAIN_PATH/fullchain.cer"
fi
if [ -z "$CERT_PFX_PATH" ] ; then
CERT_PFX_PATH="$domainhome/$domain.pfx"
CERT_PFX_PATH="$DOMAIN_PATH/$domain.pfx"
fi
if [ -z "$TLS_CONF" ] ; then
TLS_CONF="$domainhome/tls.valdation.conf"
TLS_CONF="$DOMAIN_PATH/tls.valdation.conf"
fi
if [ -z "$TLS_CERT" ] ; then
TLS_CERT="$domainhome/tls.valdation.cert"
TLS_CERT="$DOMAIN_PATH/tls.valdation.cert"
fi
if [ -z "$TLS_KEY" ] ; then
TLS_KEY="$domainhome/tls.valdation.key"
TLS_KEY="$DOMAIN_PATH/tls.valdation.key"
fi
if [ -z "$TLS_CSR" ] ; then
TLS_CSR="$domainhome/tls.valdation.csr"
TLS_CSR="$DOMAIN_PATH/tls.valdation.csr"
fi
}
@ -1326,7 +1409,7 @@ _clearupwebbroot() {
issue() {
if [ -z "$2" ] ; then
echo "Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ "
_usage "Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ "
return 1
fi
Le_Webroot="$1"
@ -1350,7 +1433,10 @@ issue() {
Le_Webroot="dns_cx"
fi
_initpath $Le_Domain
if [ ! "$IS_RENEW" ] ; then
_initpath $Le_Domain "$Le_Keylength"
mkdir -p "$DOMAIN_PATH"
fi
if [ -f "$DOMAIN_CONF" ] ; then
Le_NextRenewTime=$(_readdomainconf Le_NextRenewTime)
@ -1482,7 +1568,7 @@ issue() {
_savedomainconf "Le_Keylength" "$Le_Keylength"
if ! createCSR $Le_Domain $Le_Alt ; then
if ! _createcsr "$Le_Domain" "$Le_Alt" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF" ; then
_err "Create CSR error."
_clearup
return 1
@ -1861,7 +1947,7 @@ issue() {
echo "$BEGIN_CERT" > "$CERT_PATH"
_get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH"
echo "$END_CERT" >> "$CERT_PATH"
_info "Cert success."
_info "$(__green "Cert success.")"
cat "$CERT_PATH"
_info "Your cert is in $CERT_PATH"
@ -1918,22 +2004,26 @@ issue() {
if [ "$Le_RealCertPath$Le_RealKeyPath$Le_RealCACertPath$Le_ReloadCmd$Le_RealFullChainPath" ] ; then
installcert $Le_Domain "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath"
_installcert
fi
}
#domain [isEcc]
renew() {
Le_Domain="$1"
if [ -z "$Le_Domain" ] ; then
_err "Usage: $PROJECT_ENTRY --renew -d domain.com"
_usage "Usage: $PROJECT_ENTRY --renew -d domain.com [--ecc]"
return 1
fi
_initpath $Le_Domain
_info "Renew: $Le_Domain"
_isEcc="$2"
_initpath $Le_Domain "$_isEcc"
_info "Renew: '$Le_Domain'"
if [ ! -f "$DOMAIN_CONF" ] ; then
_info "$Le_Domain is not a issued domain, skip."
_info "'$Le_Domain' is not a issued domain, skip."
return 0;
fi
@ -1961,10 +2051,15 @@ renewAll() {
_stopRenewOnError="$1"
_debug "_stopRenewOnError" "$_stopRenewOnError"
_ret="0"
for d in $(ls -F ${CERT_HOME}/ | grep [^.].*[.].*/$ ) ; do
d=$(echo $d | cut -d '/' -f 1)
(
renew "$d"
(
if _endswith $d "$ECC_SUFFIX" ; then
_isEcc=$(echo $d | cut -d "$ECC_SEP" -f 2)
d=$(echo $d | cut -d "$ECC_SEP" -f 1)
fi
renew "$d" "$_isEcc"
)
rc="$?"
_debug "Return code: $rc"
@ -1990,14 +2085,18 @@ list() {
_sep="|"
if [ "$_raw" ] ; then
printf "Main_Domain${_sep}SAN_Domains${_sep}Created${_sep}Renew\n"
printf "Main_Domain${_sep}KeyLength${_sep}SAN_Domains${_sep}Created${_sep}Renew\n"
for d in $(ls -F ${CERT_HOME}/ | grep [^.].*[.].*/$ ) ; do
d=$(echo $d | cut -d '/' -f 1)
(
_initpath $d
if _endswith $d "$ECC_SUFFIX" ; then
_isEcc=$(echo $d | cut -d "$ECC_SEP" -f 2)
d=$(echo $d | cut -d "$ECC_SEP" -f 1)
fi
_initpath $d "$_isEcc"
if [ -f "$DOMAIN_CONF" ] ; then
. "$DOMAIN_CONF"
printf "$Le_Domain${_sep}$Le_Alt${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr\n"
printf "$Le_Domain${_sep}\"$Le_Keylength\"${_sep}$Le_Alt${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr\n"
fi
)
done
@ -2005,7 +2104,7 @@ list() {
if _exists column ; then
list "raw" | column -t -s "$_sep"
else
list "raw" | tr '|' '\t'
list "raw" | tr "$_sep" '\t'
fi
fi
@ -2015,7 +2114,7 @@ list() {
installcert() {
Le_Domain="$1"
if [ -z "$Le_Domain" ] ; then
echo "Usage: $PROJECT_ENTRY --installcert -d domain.com [--certpath cert-file-path] [--keypath key-file-path] [--capath ca-cert-file-path] [ --reloadCmd reloadCmd] [--fullchainpath fullchain-path]"
_usage "Usage: $PROJECT_ENTRY --installcert -d domain.com [--ecc] [--certpath cert-file-path] [--keypath key-file-path] [--capath ca-cert-file-path] [ --reloadCmd reloadCmd] [--fullchainpath fullchain-path]"
return 1
fi
@ -2024,8 +2123,19 @@ installcert() {
Le_RealCACertPath="$4"
Le_ReloadCmd="$5"
Le_RealFullChainPath="$6"
_isEcc="$7"
_initpath $Le_Domain
_initpath $Le_Domain "$_isEcc"
if [ ! -d "$DOMAIN_PATH" ] ; then
_err "Domain is not valid:'$Le_Domain'"
return 1
fi
_installcert
}
_installcert() {
_savedomainconf "Le_RealCertPath" "$Le_RealCertPath"
_savedomainconf "Le_RealCACertPath" "$Le_RealCACertPath"
@ -2053,7 +2163,7 @@ installcert() {
if [ "$Le_RealCertPath" ] ; then
_installed=1
_info "Installing cert to:$Le_RealCertPath"
if [ -f "$Le_RealCertPath" ] ; then
if [ -f "$Le_RealCertPath" ] && [ ! "$IS_RENEW" ] ; then
cp "$Le_RealCertPath" "$Le_RealCertPath".bak
fi
cat "$CERT_PATH" > "$Le_RealCertPath"
@ -2066,7 +2176,7 @@ installcert() {
echo "" >> "$Le_RealCACertPath"
cat "$CA_CERT_PATH" >> "$Le_RealCACertPath"
else
if [ -f "$Le_RealCACertPath" ] ; then
if [ -f "$Le_RealCACertPath" ] && [ ! "$IS_RENEW" ] ; then
cp "$Le_RealCACertPath" "$Le_RealCACertPath".bak
fi
cat "$CA_CERT_PATH" > "$Le_RealCACertPath"
@ -2077,7 +2187,7 @@ installcert() {
if [ "$Le_RealKeyPath" ] ; then
_installed=1
_info "Installing key to:$Le_RealKeyPath"
if [ -f "$Le_RealKeyPath" ] ; then
if [ -f "$Le_RealKeyPath" ] && [ ! "$IS_RENEW" ] ; then
cp "$Le_RealKeyPath" "$Le_RealKeyPath".bak
fi
cat "$CERT_KEY_PATH" > "$Le_RealKeyPath"
@ -2086,7 +2196,7 @@ installcert() {
if [ "$Le_RealFullChainPath" ] ; then
_installed=1
_info "Installing full chain to:$Le_RealFullChainPath"
if [ -f "$Le_RealFullChainPath" ] ; then
if [ -f "$Le_RealFullChainPath" ] && [ ! "$IS_RENEW" ] ; then
cp "$Le_RealFullChainPath" "$Le_RealFullChainPath".bak
fi
cat "$CERT_FULLCHAIN_PATH" > "$Le_RealFullChainPath"
@ -2096,7 +2206,7 @@ installcert() {
_installed=1
_info "Run Le_ReloadCmd: $Le_ReloadCmd"
if (cd "$DOMAIN_PATH" && eval "$Le_ReloadCmd") ; then
_info "Reload success."
_info "$(__green "Reload success")"
else
_err "Reload error for :$Le_Domain"
fi
@ -2158,11 +2268,13 @@ uninstallcronjob() {
revoke() {
Le_Domain="$1"
if [ -z "$Le_Domain" ] ; then
echo "Usage: $PROJECT_ENTRY --revoke -d domain.com"
_usage "Usage: $PROJECT_ENTRY --revoke -d domain.com"
return 1
fi
_initpath $Le_Domain
_isEcc="$2"
_initpath $Le_Domain "$_isEcc"
if [ ! -f "$DOMAIN_CONF" ] ; then
_err "$Le_Domain is not a issued domain, skip."
return 1;
@ -2341,7 +2453,7 @@ _setShebang() {
_file="$1"
_shebang="$2"
if [ -z "$_shebang" ] ; then
_err "Usage: file shebang"
_usage "Usage: file shebang"
return 1
fi
cp "$_file" "$_file.tmp"
@ -2592,6 +2704,7 @@ Parameters:
--stopRenewOnError, -se Only valid for '--renewall' command. Stop if one cert has error in renewal.
--insecure Do not check the server certificate, in some devices, the api server's certificate may not be trusted.
--nocron Only valid for '--install' command, which means: do not install the default cron job. In this case, the certs will not be renewed automatically.
--ecc Specifies to use the ECC cert. Valid for '--installcert', '--renew', '--revoke', '--toPkcs' and '--createCSR'
"
}
@ -2665,6 +2778,7 @@ _process() {
_stopRenewOnError=""
_insecure=""
_nocron=""
_ecc=""
while [ ${#} -gt 0 ] ; do
case "${1}" in
@ -2908,6 +3022,10 @@ _process() {
--nocron)
_nocron="1"
;;
--ecc)
_ecc="isEcc"
;;
*)
_err "Unknown parameter : $1"
return 1
@ -2929,16 +3047,16 @@ _process() {
issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath"
;;
installcert)
installcert "$_domain" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath"
installcert "$_domain" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath" "$_ecc"
;;
renew)
renew "$_domain"
renew "$_domain" "$_ecc"
;;
renewAll)
renewAll "$_stopRenewOnError"
;;
revoke)
revoke "$_domain"
revoke "$_domain" "$_ecc"
;;
list)
list "$_listraw"
@ -2947,7 +3065,7 @@ _process() {
uninstallcronjob) uninstallcronjob ;;
cron) cron ;;
toPkcs)
toPkcs "$_domain" "$_password"
toPkcs "$_domain" "$_password" "$_ecc"
;;
createAccountKey)
createAccountKey "$_domain" "$_accountkeylength"
@ -2956,7 +3074,7 @@ _process() {
createDomainKey "$_domain" "$_keylength"
;;
createCSR)
createCSR "$_domain" "$_altdomains"
createCSR "$_domain" "$_altdomains" "$_ecc"
;;
*)