support tls (#215)

* support tls-sni-01
'--tls'  and '--tlsport'

* fix tls doc
This commit is contained in:
neil 2016-06-17 13:23:44 +08:00 committed by GitHub
parent 869578ce4a
commit e22bcf7cb4
2 changed files with 250 additions and 57 deletions

View File

@ -170,6 +170,20 @@ acme.sh --issue --standalone -d aa.com -d www.aa.com -d cp.aa.com
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
# Use Standalone tls server to issue cert
**(requires you be root/sudoer, or you have permission to listen tcp 443 port)**
acme.sh supports `tls-sni-01` validation.
The tcp `443` port **MUST** be free to listen, otherwise you will be prompted to free the `443` port and try again.
```bash
acme.sh --issue --tls -d aa.com -d www.aa.com -d cp.aa.com
```
More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
# Use Apache mode # Use Apache mode
**(requires you be root/sudoer, since it is required to interact with apache server)** **(requires you be root/sudoer, since it is required to interact with apache server)**

293
acme.sh
View File

@ -1,6 +1,6 @@
#!/usr/bin/env sh #!/usr/bin/env sh
VER=2.2.6 VER=2.2.7
PROJECT_NAME="acme.sh" PROJECT_NAME="acme.sh"
@ -17,6 +17,10 @@ STAGE_CA="https://acme-staging.api.letsencrypt.org"
VTYPE_HTTP="http-01" VTYPE_HTTP="http-01"
VTYPE_DNS="dns-01" VTYPE_DNS="dns-01"
VTYPE_TLS="tls-sni-01"
VTYPE_TLS2="tls-sni-02"
W_TLS="tls"
BEGIN_CSR="-----BEGIN CERTIFICATE REQUEST-----" BEGIN_CSR="-----BEGIN CERTIFICATE REQUEST-----"
END_CSR="-----END CERTIFICATE REQUEST-----" END_CSR="-----END CERTIFICATE REQUEST-----"
@ -251,7 +255,7 @@ _dbase64() {
fi fi
} }
#Usage: hashalg #Usage: hashalg [outputhex]
#Output Base64-encoded digest #Output Base64-encoded digest
_digest() { _digest() {
alg="$1" alg="$1"
@ -260,8 +264,14 @@ _digest() {
return 1 return 1
fi fi
outputhex="$2"
if [ "$alg" = "sha256" ] ; then if [ "$alg" = "sha256" ] ; then
openssl dgst -sha256 -binary | _base64 if [ "$outputhex" ] ; then
echo $(openssl dgst -sha256 -hex | cut -d = -f 2)
else
openssl dgst -sha256 -binary | _base64
fi
else else
_err "$alg is not supported yet" _err "$alg is not supported yet"
return 1 return 1
@ -288,6 +298,86 @@ _sign() {
} }
# _createkey 2048|ec-256 file
_createkey() {
length="$1"
f="$2"
isec=""
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
if [ "$length" = "384" ] ; then
eccname="secp384r1"
fi
if [ "$length" = "521" ] ; then
eccname="secp521r1"
fi
_info "Using ec name: $eccname"
fi
#generate account key
if [ "$isec" ] ; then
openssl ecparam -name $eccname -genkey 2>/dev/null > "$f"
else
openssl genrsa $length 2>/dev/null > "$f"
fi
}
#_createcsr cn san_list keyfile csrfile conf
_createcsr() {
_debug _createcsr
domain="$1"
domainlist="$2"
key="$3"
csr="$4"
csrconf="$5"
_debug2 domain "$domain"
_debug2 domainlist "$domainlist"
if [ -z "$domainlist" ] || [ "$domainlist" = "no" ]; then
#single domain
_info "Single domain" "$domain"
printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\n" > "$csrconf"
openssl req -new -sha256 -key "$key" -subj "/CN=$domain" -config "$csrconf" -out "$csr"
else
if _contains "$domainlist" "," ; then
alt="DNS:$(echo $domainlist | sed "s/,/,DNS:/g")"
else
alt="DNS:$domainlist"
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"
openssl req -new -sha256 -key "$key" -subj "/CN=$domain" -config "$csrconf" -out "$csr"
fi
}
#_signcsr key csr conf cert
_signcsr() {
key="$1"
csr="$2"
conf="$3"
cert="$4"
openssl x509 -req -days 365 -in "$csr" -signkey "$key" -extensions v3_req -extfile "$conf" -out "$cert"
}
_ss() { _ss() {
_port="$1" _port="$1"
@ -364,7 +454,7 @@ createAccountKey() {
return return
else else
#generate account key #generate account key
openssl genrsa $length 2>/dev/null > "$ACCOUNT_KEY_PATH" _createkey $length "$ACCOUNT_KEY_PATH"
fi fi
} }
@ -378,45 +468,12 @@ createDomainKey() {
fi fi
domain=$1 domain=$1
length=$2
isec=""
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
if [ "$length" = "384" ] ; then
eccname="secp384r1"
fi
if [ "$length" = "521" ] ; then
eccname="secp521r1"
fi
_info "Using ec name: $eccname"
fi
_initpath $domain _initpath $domain
length=$2
if [ ! -f "$CERT_KEY_PATH" ] || ( [ "$FORCE" ] && ! [ "$IS_RENEW" ] ); then if [ ! -f "$CERT_KEY_PATH" ] || ( [ "$FORCE" ] && ! [ "$IS_RENEW" ] ); then
#generate account key _createkey "$length" "$CERT_KEY_PATH"
if [ "$isec" ] ; then
openssl ecparam -name $eccname -genkey 2>/dev/null > "$CERT_KEY_PATH"
else
openssl genrsa $length 2>/dev/null > "$CERT_KEY_PATH"
fi
else else
if [ "$IS_RENEW" ] ; then if [ "$IS_RENEW" ] ; then
_info "Domain key exists, skip" _info "Domain key exists, skip"
@ -447,19 +504,8 @@ createCSR() {
return return
fi fi
if [ -z "$domainlist" ] || [ "$domainlist" = "no" ]; then _createcsr "$domain" "$domainlist" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"
#single domain
_info "Single domain" "$domain"
printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\n" > "$DOMAIN_SSL_CONF"
openssl req -new -sha256 -key "$CERT_KEY_PATH" -subj "/CN=$domain" -config "$DOMAIN_SSL_CONF" -out "$CSR_PATH"
else
alt="DNS:$(echo $domainlist | sed "s/,/,DNS:/g")"
#multi
_info "Multi domain" "$alt"
printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\n[SAN]\nsubjectAltName=$alt" > "$DOMAIN_SSL_CONF"
openssl req -new -sha256 -key "$CERT_KEY_PATH" -subj "/CN=$domain" -reqexts SAN -config "$DOMAIN_SSL_CONF" -out "$CSR_PATH"
fi
} }
_urlencode() { _urlencode() {
@ -819,9 +865,52 @@ _stopserver(){
if [ -z "$pid" ] ; then if [ -z "$pid" ] ; then
return return
fi fi
_get "http://localhost:$Le_HTTPPort" >/dev/null 2>&1
_get "http://localhost:$Le_HTTPPort" >/dev/null 2>&1
_get "http://localhost:$Le_TLSPort" >/dev/null 2>&1
}
# _starttlsserver san_a san_b port content
_starttlsserver() {
_info "Starting tls server."
san_a="$1"
san_b="$2"
port="$3"
content="$4"
_debug san_a "$san_a"
_debug san_b "$san_b"
_debug port "$port"
#create key TLS_KEY
if ! _createkey "2048" "$TLS_KEY" ; then
_err "Create tls validation key error."
return 1
fi
#create csr
alt="$san_a"
if [ "$san_b" ] ; then
alt="$alt,$san_b"
fi
if ! _createcsr "tls.acme.sh" "$alt" "$TLS_KEY" "$TLS_CSR" "$TLS_CONF" ; then
_err "Create tls validation csr error."
return 1
fi
#self signed
if ! _signcsr "$TLS_KEY" "$TLS_CSR" "$TLS_CONF" "$TLS_CERT" ; then
_err "Create tls validation cert error."
return 1
fi
#start openssl
(printf "HTTP/1.1 200 OK\r\n\r\n$content" | openssl s_server -cert "$TLS_CERT" -key "$TLS_KEY" -accept $port >/dev/null 2>&1) &
serverproc="$!"
sleep 2
_debug serverproc $serverproc
} }
_initpath() { _initpath() {
@ -935,6 +1024,20 @@ _initpath() {
if [ -z "$CERT_PFX_PATH" ] ; then if [ -z "$CERT_PFX_PATH" ] ; then
CERT_PFX_PATH="$domainhome/$domain.pfx" CERT_PFX_PATH="$domainhome/$domain.pfx"
fi fi
if [ -z "$TLS_CONF" ] ; then
TLS_CONF="$domainhome/tls.valdation.conf"
fi
if [ -z "$TLS_CERT" ] ; then
TLS_CERT="$domainhome/tls.valdation.cert"
fi
if [ -z "$TLS_KEY" ] ; then
TLS_KEY="$domainhome/tls.valdation.key"
fi
if [ -z "$TLS_CSR" ] ; then
TLS_CSR="$domainhome/tls.valdation.csr"
fi
} }
@ -1073,6 +1176,12 @@ _clearup() {
_stopserver $serverproc _stopserver $serverproc
serverproc="" serverproc=""
_restoreApache _restoreApache
if [ -z "$DEBUG" ] ; then
rm -f "$TLS_CONF"
rm -f "$TLS_CERT"
rm -f "$TLS_KEY"
rm -f "$TLS_CSR"
fi
} }
# webroot removelevel tokenfile # webroot removelevel tokenfile
@ -1171,6 +1280,24 @@ issue() {
fi fi
fi fi
if _hasfield "$Le_Webroot" "$W_TLS" ; then
_info "Standalone tls mode."
if [ -z "$Le_TLSPort" ] ; then
Le_TLSPort=443
else
_savedomainconf "Le_TLSPort" "$Le_TLSPort"
fi
netprc="$(_ss "$Le_TLSPort" | grep "$Le_TLSPort")"
if [ "$netprc" ] ; then
_err "$netprc"
_err "tcp port $Le_TLSPort is already used by $(echo "$netprc" | cut -d : -f 4)"
_err "Please stop it first"
return 1
fi
fi
if _hasfield "$Le_Webroot" "apache" ; then if _hasfield "$Le_Webroot" "apache" ; then
if ! _setApache ; then if ! _setApache ; then
_err "set up apache error. Report error to me." _err "set up apache error. Report error to me."
@ -1263,6 +1390,11 @@ issue() {
if _startswith "$_currentRoot" "dns" ; then if _startswith "$_currentRoot" "dns" ; then
vtype="$VTYPE_DNS" vtype="$VTYPE_DNS"
fi fi
if [ "$_currentRoot" = "$W_TLS" ] ; then
vtype="$VTYPE_TLS"
fi
_info "Getting token for domain" $d _info "Getting token for domain" $d
if ! _send_signed_request "$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$d\"}}" ; then if ! _send_signed_request "$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$d\"}}" ; then
@ -1406,7 +1538,7 @@ issue() {
_debug "keyauthorization" "$keyauthorization" _debug "keyauthorization" "$keyauthorization"
_debug "uri" "$uri" _debug "uri" "$uri"
removelevel="" removelevel=""
token="" token="$(printf "%s" "$keyauthorization" | cut -d '.' -f 1)"
_debug "_currentRoot" "$_currentRoot" _debug "_currentRoot" "$_currentRoot"
@ -1439,7 +1571,6 @@ issue() {
_debug wellknown_path "$wellknown_path" _debug wellknown_path "$wellknown_path"
token="$(printf "%s" "$keyauthorization" | cut -d '.' -f 1)"
_debug "writing token:$token to $wellknown_path/$token" _debug "writing token:$token to $wellknown_path/$token"
mkdir -p "$wellknown_path" mkdir -p "$wellknown_path"
@ -1451,6 +1582,37 @@ issue() {
fi fi
fi fi
elif [ "$vtype" = "$VTYPE_TLS" ] ; then
#create A
#_hash_A="$(printf "%s" $token | _digest "sha256" "hex" )"
#_debug2 _hash_A "$_hash_A"
#_x="$(echo $_hash_A | cut -c 1-32)"
#_debug2 _x "$_x"
#_y="$(echo $_hash_A | cut -c 33-64)"
#_debug2 _y "$_y"
#_SAN_A="$_x.$_y.token.acme.invalid"
#_debug2 _SAN_A "$_SAN_A"
#create B
_hash_B="$(printf "%s" $keyauthorization | _digest "sha256" "hex" )"
_debug2 _hash_B "$_hash_B"
_x="$(echo $_hash_B | cut -c 1-32)"
_debug2 _x "$_x"
_y="$(echo $_hash_B | cut -c 33-64)"
_debug2 _y "$_y"
#_SAN_B="$_x.$_y.ka.acme.invalid"
_SAN_B="$_x.$_y.acme.invalid"
_debug2 _SAN_B "$_SAN_B"
if ! _starttlsserver "$_SAN_B" "$_SAN_A" "$Le_TLSPort" "$keyauthorization" ; then
_err "Start tls server error."
_clearupwebbroot "$_currentRoot" "$removelevel" "$token"
_clearup
return 1
fi
fi fi
if ! _send_signed_request $uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}" ; then if ! _send_signed_request $uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}" ; then
@ -2203,6 +2365,7 @@ Parameters:
--webroot, -w /path/to/webroot Specifies the web root folder for web root mode. --webroot, -w /path/to/webroot Specifies the web root folder for web root mode.
--standalone Use standalone mode. --standalone Use standalone mode.
--tls Use standalone tls mode.
--apache Use apache mode. --apache Use apache mode.
--dns [dns_cf|dns_dp|dns_cx|/path/to/api/file] Use dns mode or dns api. --dns [dns_cf|dns_dp|dns_cx|/path/to/api/file] Use dns mode or dns api.
--dnssleep [60] The time in seconds to wait for all the txt records to take effect in dns api mode. Default 60 seconds. --dnssleep [60] The time in seconds to wait for all the txt records to take effect in dns api mode. Default 60 seconds.
@ -2227,6 +2390,7 @@ Parameters:
--accountkey Specifies the account key path, Only valid for the '--install' command. --accountkey Specifies the account key path, Only valid for the '--install' command.
--days Specifies the days to renew the cert when using '--issue' command. The max value is 80 days. --days Specifies the days to renew the cert when using '--issue' command. The max value is 80 days.
--httpport Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer. --httpport Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer.
--tlsport Specifies the standalone tls listening port. Only valid if the server is behind a reverse proxy or load balancer.
--listraw Only used for '--list' command, list the certs in raw format. --listraw Only used for '--list' command, list the certs in raw format.
" "
} }
@ -2277,6 +2441,7 @@ _process() {
_accountkey="" _accountkey=""
_certhome="" _certhome=""
_httpport="" _httpport=""
_tlsport=""
_dnssleep="" _dnssleep=""
_listraw="" _listraw=""
while [ ${#} -gt 0 ] ; do while [ ${#} -gt 0 ] ; do
@ -2399,6 +2564,14 @@ _process() {
_webroot="$_webroot,$wvalue" _webroot="$_webroot,$wvalue"
fi fi
;; ;;
--tls)
wvalue="$W_TLS"
if [ -z "$_webroot" ] ; then
_webroot="$wvalue"
else
_webroot="$_webroot,$wvalue"
fi
;;
--dns) --dns)
wvalue="dns" wvalue="dns"
if ! _startswith "$2" "-" ; then if ! _startswith "$2" "-" ; then
@ -2490,6 +2663,12 @@ _process() {
Le_HTTPPort="$_httpport" Le_HTTPPort="$_httpport"
shift shift
;; ;;
--tlsport )
_tlsport="$2"
Le_TLSPort="$_tlsport"
shift
;;
--listraw ) --listraw )
_listraw="raw" _listraw="raw"
;; ;;