neil 9 years ago
parent
commit
f8029e2e75
1 changed files with 1301 additions and 1301 deletions
  1. 1301 1301
      le.sh

+ 1301 - 1301
le.sh

@@ -1,1301 +1,1301 @@
-#!/usr/bin/env bash
-VER=1.1.8
-PROJECT="https://github.com/Neilpang/le"
-
-DEFAULT_CA="https://acme-v01.api.letsencrypt.org"
-DEFAULT_AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf"
-
-STAGE_CA="https://acme-staging.api.letsencrypt.org"
-
-VTYPE_HTTP="http-01"
-VTYPE_DNS="dns-01"
-
-if [ -z "$AGREEMENT" ] ; then
-  AGREEMENT="$DEFAULT_AGREEMENT"
-fi
-
-_debug() {
-
-  if [ -z "$DEBUG" ] ; then
-    return
-  fi
-  
-  if [ -z "$2" ] ; then
-    echo $1
-  else
-    echo "$1"="$2"
-  fi
-}
-
-_info() {
-  if [ -z "$2" ] ; then
-    echo "$1"
-  else
-    echo "$1"="$2"
-  fi
-}
-
-_err() {
-  if [ -z "$2" ] ; then
-    echo "$1" >&2
-  else
-    echo "$1"="$2" >&2
-  fi
-  return 1
-}
-
-_h2b() {
-  hex=$(cat)
-  i=1
-  j=2
-  while [ '1' ] ; do
-    h=$(printf $hex | cut -c $i-$j)
-    if [ -z "$h" ] ; then
-      break;
-    fi
-    printf "\x$h"
-    let "i+=2"
-    let "j+=2"
-  done
-}
-
-_base64() {
-  openssl base64 -e | tr -d '\n'
-}
-
-#domain [2048]  
-createAccountKey() {
-  _info "Creating account key"
-  if [ -z "$1" ] ; then
-    echo Usage: createAccountKey account-domain  [2048]
-    return
-  fi
-  
-  account=$1
-  length=$2
-  
-  if [[ "$length" == "ec-"* ]] ; then
-    length=2048
-  fi
-  
-  if [ -z "$2" ] ; then
-    _info "Use default length 2048"
-    length=2048
-  fi
-  _initpath
-  
-  if [ -f "$ACCOUNT_KEY_PATH" ] ; then
-    _info "Account key exists, skip"
-    return
-  else
-    #generate account key
-    openssl genrsa $length > "$ACCOUNT_KEY_PATH"
-  fi
-
-}
-
-#domain length
-createDomainKey() {
-  _info "Creating domain key"
-  if [ -z "$1" ] ; then
-    echo Usage: createDomainKey domain  [2048]
-    return
-  fi
-  
-  domain=$1
-  length=$2
-  isec=""
-  if [[ "$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
-  
-  if [ ! -f "$CERT_KEY_PATH" ] || ( [ "$FORCE" ] && ! [ "$IS_RENEW" ] ); then 
-    #generate account key
-    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
-    if [ "$IS_RENEW" ] ; then
-      _info "Domain key exists, skip"
-      return 0
-    else
-      _err "Domain key exists, do you want to overwrite the key?"
-      _err "Set FORCE=1, and try again."
-      return 1
-    fi
-  fi
-
-}
-
-# domain  domainlist
-createCSR() {
-  _info "Creating csr"
-  if [ -z "$1" ] ; then
-    echo Usage: $0 domain  [domainlist]
-    return
-  fi
-  domain=$1
-  _initpath $domain
-  
-  domainlist=$2
-  
-  if [ -f "$CSR_PATH" ]  && [ "$IS_RENEW" ] && ! [ "$FORCE" ]; then
-    _info "CSR exists, skip"
-    return
-  fi
-  
-  if [ -z "$domainlist" ] ; then
-    #single domain
-    _info "Single domain" $domain
-    openssl req -new -sha256 -key "$CERT_KEY_PATH" -subj "/CN=$domain" > "$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
-
-}
-
-_b64() {
-  __n=$(cat)
-  echo $__n | tr '/+' '_-' | tr -d '= '
-}
-
-_time2str() {
-  #BSD
-  if date -u -d@$1 2>/dev/null ; then
-    return
-  fi
-  
-  #Linux
-  if date -u -r $1 2>/dev/null ; then
-    return
-  fi
-  
-}
-
-_send_signed_request() {
-  url=$1
-  payload=$2
-  needbase64=$3
-  
-  _debug url $url
-  _debug payload "$payload"
-  
-  CURL_HEADER="$LE_WORKING_DIR/curl.header"
-  dp="$LE_WORKING_DIR/curl.dump"
-  CURL="curl --silent --dump-header $CURL_HEADER "
-  if [ "$DEBUG" ] ; then
-    CURL="$CURL --trace-ascii $dp "
-  fi
-  payload64=$(echo -n $payload | _base64 | _b64)
-  _debug payload64 $payload64
-  
-  nonceurl="$API/directory"
-  nonce="$($CURL -I $nonceurl | grep -o "^Replay-Nonce:.*$" | tr -d "\r\n" | cut -d ' ' -f 2)"
-
-  _debug nonce "$nonce"
-  
-  protected="$(printf "$HEADERPLACE" | sed "s/NONCE/$nonce/" )"
-  _debug protected "$protected"
-  
-  protected64="$(printf "$protected" | _base64 | _b64)"
-  _debug protected64 "$protected64"
-  
-  sig=$(echo -n "$protected64.$payload64" |  openssl   dgst   -sha256  -sign  $ACCOUNT_KEY_PATH | _base64 | _b64)
-  _debug sig "$sig"
-  
-  body="{\"header\": $HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
-  _debug body "$body"
-  
-  if [ "$needbase64" ] ; then
-    response="$($CURL -X POST --data "$body" $url | _base64)"
-  else
-    response="$($CURL -X POST --data "$body" $url)"
-  fi
-
-  responseHeaders="$(cat $CURL_HEADER)"
-  
-  _debug responseHeaders "$responseHeaders"
-  _debug response  "$response"
-  code="$(grep ^HTTP $CURL_HEADER | tail -1 | cut -d " " -f 2 | tr -d "\r\n" )"
-  _debug code $code
-
-}
-
-_get() {
-  url="$1"
-  _debug url $url
-  response="$(curl --silent $url)"
-  ret=$?
-  _debug response  "$response"
-  code="$(echo $response | grep -o '"status":[0-9]\+' | cut -d : -f 2)"
-  _debug code $code
-  return $ret
-}
-
-#setopt "file"  "opt"  "="  "value" [";"]
-_setopt() {
-  __conf="$1"
-  __opt="$2"
-  __sep="$3"
-  __val="$4"
-  __end="$5"
-  if [ -z "$__opt" ] ; then 
-    echo usage: _setopt  '"file"  "opt"  "="  "value" [";"]'
-    return
-  fi
-  if [ ! -f "$__conf" ] ; then
-    touch "$__conf"
-  fi
-
-  if grep -H -n "^$__opt$__sep" "$__conf" > /dev/null ; then
-    _debug OK
-    if [[ "$__val" == *"&"* ]] ; then
-      __val="$(echo $__val | sed 's/&/\\&/g')"
-    fi
-    text="$(cat $__conf)"
-    printf "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
-
-  elif grep -H -n "^#$__opt$__sep" "$__conf" > /dev/null ; then
-    if [[ "$__val" == *"&"* ]] ; then
-      __val="$(echo $__val | sed 's/&/\\&/g')"
-    fi
-    text="$(cat $__conf)"
-    printf "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
-
-  else
-    _debug APP
-    echo "$__opt$__sep$__val$__end" >> "$__conf"
-  fi
-  _debug "$(grep -H -n "^$__opt$__sep" $__conf)"
-}
-
-#_savedomainconf   key  value
-#save to domain.conf
-_savedomainconf() {
-  key="$1"
-  value="$2"
-  if [ "$DOMAIN_CONF" ] ; then
-    _setopt $DOMAIN_CONF "$key" "=" "$value"
-  else
-    _err "DOMAIN_CONF is empty, can not save $key=$value"
-  fi
-}
-
-#_saveaccountconf  key  value
-_saveaccountconf() {
-  key="$1"
-  value="$2"
-  if [ "$ACCOUNT_CONF_PATH" ] ; then
-    _setopt $ACCOUNT_CONF_PATH "$key" "=" "$value"
-  else
-    _err "ACCOUNT_CONF_PATH is empty, can not save $key=$value"
-  fi
-}
-
-_startserver() {
-  content="$1"
-  _NC="nc -q 1"
-  if nc -h 2>&1 | grep "nmap.org/ncat" >/dev/null ; then
-    _NC="nc"
-  fi
-#  while true ; do
-    if [ "$DEBUG" ] ; then
-      echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -l -p $Le_HTTPPort -vv
-    else
-      echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -l -p $Le_HTTPPort > /dev/null
-    fi
-#  done
-}
-
-_stopserver() {
-  pid="$1"
-
-}
-
-_initpath() {
-
-  if [ -z "$LE_WORKING_DIR" ]; then
-    LE_WORKING_DIR=$HOME/.le
-  fi
-  
-  if [ -z "$ACCOUNT_CONF_PATH" ] ; then
-    ACCOUNT_CONF_PATH="$LE_WORKING_DIR/account.conf"
-  fi
-  
-  if [ -f "$ACCOUNT_CONF_PATH" ] ; then
-    source "$ACCOUNT_CONF_PATH"
-  fi
-
-  if [ -z "$API" ] ; then
-    if [ -z "$STAGE" ] ; then
-      API="$DEFAULT_CA"
-    else
-      API="$STAGE_CA"
-      _info "Using stage api:$API"
-    fi  
-  fi
-  
-  if [ -z "$ACME_DIR" ] ; then
-    ACME_DIR="/home/.acme"
-  fi
-  
-  if [ -z "$APACHE_CONF_BACKUP_DIR" ] ; then
-    APACHE_CONF_BACKUP_DIR="$LE_WORKING_DIR/"
-  fi
-  
-  domain="$1"
-  mkdir -p "$LE_WORKING_DIR"
-  
-  if [ -z "$ACCOUNT_KEY_PATH" ] ; then
-    ACCOUNT_KEY_PATH="$LE_WORKING_DIR/account.key"
-  fi
-
-  if [ -z "$domain" ] ; then
-    return 0
-  fi
-  
-  domainhome="$LE_WORKING_DIR/$domain"
-  mkdir -p "$domainhome"
-
-  if [ -z "$DOMAIN_PATH" ] ; then
-    DOMAIN_PATH="$domainhome"
-  fi
-  if [ -z "$DOMAIN_CONF" ] ; then
-    DOMAIN_CONF="$domainhome/$Le_Domain.conf"
-  fi
-  
-  if [ -z "$DOMAIN_SSL_CONF" ] ; then
-    DOMAIN_SSL_CONF="$domainhome/$Le_Domain.ssl.conf"
-  fi
-  
-  if [ -z "$CSR_PATH" ] ; then
-    CSR_PATH="$domainhome/$domain.csr"
-  fi
-  if [ -z "$CERT_KEY_PATH" ] ; then 
-    CERT_KEY_PATH="$domainhome/$domain.key"
-  fi
-  if [ -z "$CERT_PATH" ] ; then
-    CERT_PATH="$domainhome/$domain.cer"
-  fi
-  if [ -z "$CA_CERT_PATH" ] ; then
-    CA_CERT_PATH="$domainhome/ca.cer"
-  fi
-
-}
-
-
-_apachePath() {
-  httpdroot="$(apachectl -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"' )"
-  httpdconfname="$(apachectl -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | tr -d '"' )"
-  httpdconf="$httpdroot/$httpdconfname"
-  if [ ! -f $httpdconf ] ; then
-    _err "Apache Config file not found" $httpdconf
-    return 1
-  fi
-  return 0
-}
-
-_restoreApache() {
-  if [ -z "$usingApache" ] ; then
-    return 0
-  fi
-  _initpath
-  if ! _apachePath ; then
-    return 1
-  fi
-  
-  if [ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ] ; then
-    _debug "No config file to restore."
-    return 0
-  fi
-  
-  cp -p "$APACHE_CONF_BACKUP_DIR/$httpdconfname" "$httpdconf"
-  if ! apachectl  -t ; then
-    _err "Sorry, restore apache config error, please contact me."
-    return 1;
-  fi
-  rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
-  return 0  
-}
-
-_setApache() {
-  _initpath
-  if ! _apachePath ; then
-    return 1
-  fi
-
-  #backup the conf
-  _debug "Backup apache config file" $httpdconf
-  cp -p $httpdconf $APACHE_CONF_BACKUP_DIR/
-  _info "JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
-  _info "In case there is an error that can not be restored automatically, you may try restore it yourself."
-  _info "The backup file will be deleted on sucess, just forget it."
-  
-  #add alias
-  echo "
-Alias /.well-known/acme-challenge  $ACME_DIR
-
-<Directory $ACME_DIR >
-Require all granted
-</Directory>
-  " >> $httpdconf
-  
-  if ! apachectl  -t ; then
-    _err "Sorry, apache config error, please contact me."
-    _restoreApache
-    return 1;
-  fi
-  
-  if [ ! -d "$ACME_DIR" ] ; then
-    mkdir -p "$ACME_DIR"
-    chmod 755 "$ACME_DIR"
-  fi
-  
-  if ! apachectl  graceful ; then
-    _err "Sorry, apachectl  graceful error, please contact me."
-    _restoreApache
-    return 1;
-  fi
-  usingApache="1"
-  return 0
-}
-
-_clearup () {
-  _stopserver $serverproc
-  serverproc=""
-  _restoreApache
-}
-
-# webroot  removelevel tokenfile
-_clearupwebbroot() {
-  __webroot="$1"
-  if [ -z "$__webroot" ] ; then
-    _debug "no webroot specified, skip"
-    return 0
-  fi
-  
-  if [ "$2" == '1' ] ; then
-    _debug "remove $__webroot/.well-known"
-    rm -rf "$__webroot/.well-known"
-  elif [ "$2" == '2' ] ; then
-    _debug "remove $__webroot/.well-known/acme-challenge"
-    rm -rf "$__webroot/.well-known/acme-challenge"
-  elif [ "$2" == '3' ] ; then
-    _debug "remove $__webroot/.well-known/acme-challenge/$3"
-    rm -rf "$__webroot/.well-known/acme-challenge/$3"
-  else
-    _info "Skip for removelevel:$2"
-  fi
-  
-  return 0
-
-}
-
-issue() {
-  if [ -z "$2" ] ; then
-    _err "Usage: le  issue  webroot|no|apache|dns   a.com  [www.a.com,b.com,c.com]|no   [key-length]|no"
-    return 1
-  fi
-  Le_Webroot="$1"
-  Le_Domain="$2"
-  Le_Alt="$3"
-  Le_Keylength="$4"
-  Le_RealCertPath="$5"
-  Le_RealKeyPath="$6"
-  Le_RealCACertPath="$7"
-  Le_ReloadCmd="$8"
-
-  
-  _initpath $Le_Domain
-  
-  if [ -f "$DOMAIN_CONF" ] ; then
-    Le_NextRenewTime=$(grep "^Le_NextRenewTime=" "$DOMAIN_CONF" | cut -d '=' -f 2)
-    if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then 
-      _info "Skip, Next renewal time is: $(grep "^Le_NextRenewTimeStr" "$DOMAIN_CONF" | cut -d '=' -f 2)"
-      return 2
-    fi
-  fi
-  
-  if [ "$Le_Alt" == "no" ] ; then
-    Le_Alt=""
-  fi
-  if [ "$Le_Keylength" == "no" ] ; then
-    Le_Keylength=""
-  fi
-  if [ "$Le_RealCertPath" == "no" ] ; then
-    Le_RealCertPath=""
-  fi
-  if [ "$Le_RealKeyPath" == "no" ] ; then
-    Le_RealKeyPath=""
-  fi
-  if [ "$Le_RealCACertPath" == "no" ] ; then
-    Le_RealCACertPath=""
-  fi
-  if [ "$Le_ReloadCmd" == "no" ] ; then
-    Le_ReloadCmd=""
-  fi
-  
-  _setopt "$DOMAIN_CONF"  "Le_Domain"             "="  "$Le_Domain"
-  _setopt "$DOMAIN_CONF"  "Le_Alt"                "="  "$Le_Alt"
-  _setopt "$DOMAIN_CONF"  "Le_Webroot"            "="  "$Le_Webroot"
-  _setopt "$DOMAIN_CONF"  "Le_Keylength"          "="  "$Le_Keylength"
-  _setopt "$DOMAIN_CONF"  "Le_RealCertPath"       "="  "\"$Le_RealCertPath\""
-  _setopt "$DOMAIN_CONF"  "Le_RealCACertPath"     "="  "\"$Le_RealCACertPath\""
-  _setopt "$DOMAIN_CONF"  "Le_RealKeyPath"        "="  "\"$Le_RealKeyPath\""
-  _setopt "$DOMAIN_CONF"  "Le_ReloadCmd"          "="  "\"$Le_ReloadCmd\""
-  
-  if [ "$Le_Webroot" == "no" ] ; then
-    _info "Standalone mode."
-    if ! command -v "nc" > /dev/null ; then
-      _err "Please install netcat(nc) tools first."
-      return 1
-    fi
-    
-    if [ -z "$Le_HTTPPort" ] ; then
-      Le_HTTPPort=80
-    fi
-    _setopt "$DOMAIN_CONF"  "Le_HTTPPort"             "="  "$Le_HTTPPort"
-    
-    netprc="$(ss -ntpl | grep :$Le_HTTPPort" ")"
-    if [ "$netprc" ] ; then
-      _err "$netprc"
-      _err "tcp port $Le_HTTPPort is already used by $(echo "$netprc" | cut -d :  -f 4)"
-      _err "Please stop it first"
-      return 1
-    fi
-  fi
-  
-  if [ "$Le_Webroot" == "apache" ] ; then
-    if ! _setApache ; then
-      _err "set up apache error. Report error to me."
-      return 1
-    fi
-    wellknown_path="$ACME_DIR"
-  else
-    usingApache=""
-  fi
-  
-  createAccountKey $Le_Domain $Le_Keylength
-  
-  if ! createDomainKey $Le_Domain $Le_Keylength ; then 
-    _err "Create domain key error."
-    return 1
-  fi
-  
-  if ! createCSR  $Le_Domain  $Le_Alt ; then
-    _err "Create CSR error."
-    return 1
-  fi
-
-  pub_exp=$(openssl rsa -in $ACCOUNT_KEY_PATH  -noout -text | grep "^publicExponent:"| cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1)
-  if [ "${#pub_exp}" == "5" ] ; then
-    pub_exp=0$pub_exp
-  fi
-  _debug pub_exp "$pub_exp"
-  
-  e=$(echo $pub_exp | _h2b | _base64)
-  _debug e "$e"
-  
-  modulus=$(openssl rsa -in $ACCOUNT_KEY_PATH -modulus -noout | cut -d '=' -f 2 )
-  n=$(echo $modulus| _h2b | _base64 | _b64 )
-
-  jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
-  
-  HEADER='{"alg": "RS256", "jwk": '$jwk'}'
-  HEADERPLACE='{"nonce": "NONCE", "alg": "RS256", "jwk": '$jwk'}'
-  _debug HEADER "$HEADER"
-  
-  accountkey_json=$(echo -n "$jwk" |  tr -d ' ' )
-  thumbprint=$(echo -n "$accountkey_json" | openssl dgst -sha256 -binary | _base64 | _b64)
-  
-  
-  _info "Registering account"
-  regjson='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}'
-  if [ "$ACCOUNT_EMAIL" ] ; then
-    regjson='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}'
-  fi  
-  _send_signed_request   "$API/acme/new-reg"  "$regjson"
-  
-  if [ "$code" == "" ] || [ "$code" == '201' ] ; then
-    _info "Registered"
-    echo $response > $LE_WORKING_DIR/account.json
-  elif [ "$code" == '409' ] ; then
-    _info "Already registered"
-  else
-    _err "Register account Error."
-    _clearup
-    return 1
-  fi
-  
-  vtype="$VTYPE_HTTP"
-  if [[ "$Le_Webroot" == "dns"* ]] ; then
-    vtype="$VTYPE_DNS"
-  fi
-  
-  vlist="$Le_Vlist"
-  # verify each domain
-  _info "Verify each domain"
-  sep='#'
-  if [ -z "$vlist" ] ; then
-    alldomains=$(echo "$Le_Domain,$Le_Alt" |  tr ',' ' ' )
-    for d in $alldomains   
-    do  
-      _info "Getting token for domain" $d
-      _send_signed_request "$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$d\"}}"
-      if [ ! -z "$code" ] && [ ! "$code" == '201' ] ; then
-        _err "new-authz error: $response"
-        _clearup
-        return 1
-      fi
-
-      entry="$(printf $response | egrep -o  '{[^{]*"type":"'$vtype'"[^}]*')"
-      _debug entry "$entry"
-
-      token="$(printf "$entry" | egrep -o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
-      _debug token $token
-      
-      uri="$(printf "$entry" | egrep -o '"uri":"[^"]*'| cut -d : -f 2,3 | tr -d '"' )"
-      _debug uri $uri
-      
-      keyauthorization="$token.$thumbprint"
-      _debug keyauthorization "$keyauthorization"
-
-      dvlist="$d$sep$keyauthorization$sep$uri"
-      _debug dvlist "$dvlist"
-      
-      vlist="$vlist$dvlist,"
-
-    done
-
-    #add entry
-    dnsadded=""
-    ventries=$(echo "$vlist" |  tr ',' ' ' )
-    for ventry in $ventries
-    do
-      d=$(echo $ventry | cut -d $sep -f 1)
-      keyauthorization=$(echo $ventry | cut -d $sep -f 2)
-
-      if [ "$vtype" == "$VTYPE_DNS" ] ; then
-        dnsadded='0'
-        txtdomain="_acme-challenge.$d"
-        _debug txtdomain "$txtdomain"
-        txt="$(echo -e -n $keyauthorization | openssl dgst -sha256 -binary | _base64 | _b64)"
-        _debug txt "$txt"
-        #dns
-        #1. check use api
-        d_api=""
-        if [ -f "$LE_WORKING_DIR/$d/$Le_Webroot" ] ; then
-          d_api="$LE_WORKING_DIR/$d/$Le_Webroot"
-        elif [ -f "$LE_WORKING_DIR/$d/$Le_Webroot.sh" ] ; then
-          d_api="$LE_WORKING_DIR/$d/$Le_Webroot.sh"
-        elif [ -f "$LE_WORKING_DIR/$Le_Webroot" ] ; then
-          d_api="$LE_WORKING_DIR/$Le_Webroot"
-        elif [ -f "$LE_WORKING_DIR/$Le_Webroot.sh" ] ; then
-          d_api="$LE_WORKING_DIR/$Le_Webroot.sh"
-        elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot" ] ; then
-          d_api="$LE_WORKING_DIR/dnsapi/$Le_Webroot"
-        elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh" ] ; then
-          d_api="$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh"
-        fi
-        _debug d_api "$d_api"
-        
-        if [ "$d_api" ]; then
-          _info "Found domain api file: $d_api"
-        else
-          _err "Add the following TXT record:"
-          _err "Domain: $txtdomain"
-          _err "TXT value: $txt"
-          _err "Please be aware that you prepend _acme-challenge. before your domain"
-          _err "so the resulting subdomain will be: $txtdomain"
-          continue
-        fi
-
-        if ! source $d_api ; then
-          _err "Load file $d_api error. Please check your api file and try again."
-          return 1
-        fi
-        
-        addcommand="$Le_Webroot-add"
-        if ! command -v $addcommand ; then 
-          _err "It seems that your api file is not correct, it must have a function named: $Le_Webroot"
-          return 1
-        fi
-        
-        if ! $addcommand $txtdomain $txt ; then
-          _err "Error add txt for domain:$txtdomain"
-          return 1
-        fi
-        dnsadded='1'
-      fi
-    done
-
-    if [ "$dnsadded" == '0' ] ; then
-      _setopt "$DOMAIN_CONF"  "Le_Vlist" "=" "\"$vlist\""
-      _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit."
-      _err "Please add the TXT records to the domains, and retry again."
-      return 1
-    fi
-    
-  fi
-  
-  if [ "$dnsadded" == '1' ] ; then
-    _info "Sleep 60 seconds for the txt records to take effect"
-    sleep 60
-  fi
-  
-  _debug "ok, let's start to verify"
-  ventries=$(echo "$vlist" |  tr ',' ' ' )
-  for ventry in $ventries
-  do
-    d=$(echo $ventry | cut -d $sep -f 1)
-    keyauthorization=$(echo $ventry | cut -d $sep -f 2)
-    uri=$(echo $ventry | cut -d $sep -f 3)
-    _info "Verifying:$d"
-    _debug "d" "$d"
-    _debug "keyauthorization" "$keyauthorization"
-    _debug "uri" "$uri"
-    removelevel=""
-    token=""
-    if [ "$vtype" == "$VTYPE_HTTP" ] ; then
-      if [ "$Le_Webroot" == "no" ] ; then
-        _info "Standalone mode server"
-        _startserver "$keyauthorization" &
-        serverproc="$!"
-        sleep 2
-        _debug serverproc $serverproc
-      else
-        if [ -z "$wellknown_path" ] ; then
-          wellknown_path="$Le_Webroot/.well-known/acme-challenge"
-        fi
-        _debug wellknown_path "$wellknown_path"
-        
-        if [ ! -d "$Le_Webroot/.well-known" ] ; then 
-          removelevel='1'
-        elif [ ! -d "$Le_Webroot/.well-known/acme-challenge" ] ; then 
-          removelevel='2'
-        else
-          removelevel='3'
-        fi
-        
-        token="$(echo -e -n "$keyauthorization" | cut -d '.' -f 1)"
-        _debug "writing token:$token to $wellknown_path/$token"
-
-        mkdir -p "$wellknown_path"
-        echo -n "$keyauthorization" > "$wellknown_path/$token"
-
-        webroot_owner=$(stat -c '%U:%G' $Le_Webroot)
-        _debug "Changing owner/group of .well-known to $webroot_owner"
-        chown -R $webroot_owner "$Le_Webroot/.well-known"
-        
-      fi
-    fi
-    
-    _send_signed_request $uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"
-    
-    if [ ! -z "$code" ] && [ ! "$code" == '202' ] ; then
-      _err "$d:Challenge error: $resource"
-      _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
-      _clearup
-      return 1
-    fi
-    
-    while [ "1" ] ; do
-      _debug "sleep 5 secs to verify"
-      sleep 5
-      _debug "checking"
-      
-      if ! _get $uri ; then
-        _err "$d:Verify error:$resource"
-        _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
-        _clearup
-        return 1
-      fi
-      
-      status=$(echo $response | egrep -o  '"status":"[^"]+"' | cut -d : -f 2 | tr -d '"')
-      if [ "$status" == "valid" ] ; then
-        _info "Success"
-        _stopserver $serverproc
-        serverproc=""
-        _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
-        break;
-      fi
-      
-      if [ "$status" == "invalid" ] ; then
-         error=$(echo $response | egrep -o '"error":{[^}]*}' | grep -o '"detail":"[^"]*"' | cut -d '"' -f 4)
-        _err "$d:Verify error:$error"
-        _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
-        _clearup
-        return 1;
-      fi
-      
-      if [ "$status" == "pending" ] ; then
-        _info "Pending"
-      else
-        _err "$d:Verify error:$response" 
-        _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
-        _clearup
-        return 1
-      fi
-      
-    done
-    
-  done
-
-  _clearup
-  _info "Verify finished, start to sign."
-  der="$(openssl req  -in $CSR_PATH -outform DER | _base64 | _b64)"
-  _send_signed_request "$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64"
-  
-  
-  Le_LinkCert="$(grep -i -o '^Location.*$' $CURL_HEADER | tr -d "\r\n" | cut -d " " -f 2)"
-  _setopt "$DOMAIN_CONF"  "Le_LinkCert"           "="  "$Le_LinkCert"
-
-  if [ "$Le_LinkCert" ] ; then
-    echo -----BEGIN CERTIFICATE----- > "$CERT_PATH"
-    curl --silent "$Le_LinkCert" | openssl base64 -e  >> "$CERT_PATH"
-    echo -----END CERTIFICATE-----  >> "$CERT_PATH"
-    _info "Cert success."
-    cat "$CERT_PATH"
-    
-    _info "Your cert is in $CERT_PATH"
-  fi
-  
-
-  if [ -z "$Le_LinkCert" ] ; then
-    response="$(echo $response | openssl base64 -d -A)"
-    _err "Sign failed: $(echo "$response" | grep -o  '"detail":"[^"]*"')"
-    return 1
-  fi
-  
-  _setopt "$DOMAIN_CONF"  'Le_Vlist' '=' "\"\""
-  
-  Le_LinkIssuer=$(grep -i '^Link' $CURL_HEADER | cut -d " " -f 2| cut -d ';' -f 1 | tr -d '<>' )
-  _setopt "$DOMAIN_CONF"  "Le_LinkIssuer"         "="  "$Le_LinkIssuer"
-  
-  if [ "$Le_LinkIssuer" ] ; then
-    echo -----BEGIN CERTIFICATE----- > "$CA_CERT_PATH"
-    curl --silent "$Le_LinkIssuer" | openssl base64 -e  >> "$CA_CERT_PATH"
-    echo -----END CERTIFICATE-----  >> "$CA_CERT_PATH"
-    _info "The intermediate CA cert is in $CA_CERT_PATH"
-  fi
-  
-  Le_CertCreateTime=$(date -u "+%s")
-  _setopt "$DOMAIN_CONF"  "Le_CertCreateTime"     "="  "$Le_CertCreateTime"
-  
-  Le_CertCreateTimeStr=$(date -u )
-  _setopt "$DOMAIN_CONF"  "Le_CertCreateTimeStr"  "="  "\"$Le_CertCreateTimeStr\""
-  
-  if [ ! "$Le_RenewalDays" ] ; then
-    Le_RenewalDays=80
-  fi
-  
-  _setopt "$DOMAIN_CONF"  "Le_RenewalDays"      "="  "$Le_RenewalDays"
-  
-  let "Le_NextRenewTime=Le_CertCreateTime+Le_RenewalDays*24*60*60"
-  _setopt "$DOMAIN_CONF"  "Le_NextRenewTime"      "="  "$Le_NextRenewTime"
-  
-  Le_NextRenewTimeStr=$( _time2str $Le_NextRenewTime )
-  _setopt "$DOMAIN_CONF"  "Le_NextRenewTimeStr"      "="  "\"$Le_NextRenewTimeStr\""
-
-
-  installcert $Le_Domain  "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd"
-
-}
-
-renew() {
-  Le_Domain="$1"
-  if [ -z "$Le_Domain" ] ; then
-    _err "Usage: $0  domain.com"
-    return 1
-  fi
-
-  _initpath $Le_Domain
-
-  if [ ! -f "$DOMAIN_CONF" ] ; then
-    _info "$Le_Domain is not a issued domain, skip."
-    return 0;
-  fi
-  
-  source "$DOMAIN_CONF"
-  if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then 
-    _info "Skip, Next renewal time is: $Le_NextRenewTimeStr"
-    return 2
-  fi
-  
-  IS_RENEW="1"
-  issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd"
-  local res=$?
-  IS_RENEW=""
-
-  return $res
-}
-
-renewAll() {
-  _initpath
-  _info "renewAll"
-  
-  for d in $(ls -F $LE_WORKING_DIR | grep [^.].*[.].*/$ ) ; do
-    d=$(echo $d | cut -d '/' -f 1)
-    _info "renew $d"
-    
-    Le_LinkCert=""
-    Le_Domain=""
-    Le_Alt=""
-    Le_Webroot=""
-    Le_Keylength=""
-    Le_LinkIssuer=""
-
-    Le_CertCreateTime=""
-    Le_CertCreateTimeStr=""
-    Le_RenewalDays=""
-    Le_NextRenewTime=""
-    Le_NextRenewTimeStr=""
-
-    Le_RealCertPath=""
-    Le_RealKeyPath=""
-    
-    Le_RealCACertPath=""
-
-    Le_ReloadCmd=""
-    
-    DOMAIN_PATH=""
-    DOMAIN_CONF=""
-    DOMAIN_SSL_CONF=""
-    CSR_PATH=""
-    CERT_KEY_PATH=""
-    CERT_PATH=""
-    CA_CERT_PATH=""
-    ACCOUNT_KEY_PATH=""
-    
-    wellknown_path=""
-    
-    renew "$d"  
-  done
-  
-}
-
-installcert() {
-  Le_Domain="$1"
-  if [ -z "$Le_Domain" ] ; then
-    _err "Usage: $0  domain.com  [cert-file-path]|no  [key-file-path]|no  [ca-cert-file-path]|no   [reloadCmd]|no"
-    return 1
-  fi
-
-  Le_RealCertPath="$2"
-  Le_RealKeyPath="$3"
-  Le_RealCACertPath="$4"
-  Le_ReloadCmd="$5"
-
-  _initpath $Le_Domain
-
-  _setopt "$DOMAIN_CONF"  "Le_RealCertPath"       "="  "\"$Le_RealCertPath\""
-  _setopt "$DOMAIN_CONF"  "Le_RealCACertPath"     "="  "\"$Le_RealCACertPath\""
-  _setopt "$DOMAIN_CONF"  "Le_RealKeyPath"        "="  "\"$Le_RealKeyPath\""
-  _setopt "$DOMAIN_CONF"  "Le_ReloadCmd"          "="  "\"$Le_ReloadCmd\""
-  
-  if [ "$Le_RealCertPath" ] ; then
-    if [ -f "$Le_RealCertPath" ] ; then
-      cp -p "$Le_RealCertPath" "$Le_RealCertPath".bak
-    fi
-    cat "$CERT_PATH" > "$Le_RealCertPath"
-  fi
-  
-  if [ "$Le_RealCACertPath" ] ; then
-    if [ -f "$Le_RealCACertPath" ] ; then
-      cp -p "$Le_RealCACertPath" "$Le_RealCACertPath".bak
-    fi
-    if [ "$Le_RealCACertPath" == "$Le_RealCertPath" ] ; then
-      echo "" >> "$Le_RealCACertPath"
-      cat "$CA_CERT_PATH" >> "$Le_RealCACertPath"
-    else
-      cat "$CA_CERT_PATH" > "$Le_RealCACertPath"
-    fi
-  fi
-
-
-  if [ "$Le_RealKeyPath" ] ; then
-    if [ -f "$Le_RealKeyPath" ] ; then
-      cp -p "$Le_RealKeyPath" "$Le_RealKeyPath".bak
-    fi
-    cat "$CERT_KEY_PATH" > "$Le_RealKeyPath"
-  fi
-
-  if [ "$Le_ReloadCmd" ] ; then
-    _info "Run Le_ReloadCmd: $Le_ReloadCmd"
-    (cd $DOMAIN_PATH && eval $Le_ReloadCmd)
-  fi
-
-}
-
-installcronjob() {
-  _initpath
-  _info "Installing cron job"
-  if ! crontab -l | grep 'le.sh cron' ; then 
-    if [ -f "$LE_WORKING_DIR/le.sh" ] ; then
-      lesh="\"$LE_WORKING_DIR\"/le.sh"
-    else
-      _err "Can not install cronjob, le.sh not found."
-      return 1
-    fi
-    crontab -l | { cat; echo "0 0 * * * LE_WORKING_DIR=\"$LE_WORKING_DIR\" $lesh cron > /dev/null"; } | crontab -
-  fi
-  return 0
-}
-
-uninstallcronjob() {
-  _info "Removing cron job"
-  cr="$(crontab -l | grep 'le.sh cron')"
-  if [ "$cr" ] ; then 
-    crontab -l | sed "/le.sh cron/d" | crontab -
-    LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 6 | cut -d '=' -f 2 | tr -d '"')"
-    _info LE_WORKING_DIR "$LE_WORKING_DIR"
-  fi 
-  _initpath
-  
-}
-
-
-# Detect profile file if not specified as environment variable
-_detect_profile() {
-  if [ -n "$PROFILE" -a -f "$PROFILE" ]; then
-    echo "$PROFILE"
-    return
-  fi
-
-  local DETECTED_PROFILE
-  DETECTED_PROFILE=''
-  local SHELLTYPE
-  SHELLTYPE="$(basename "/$SHELL")"
-
-  if [ "$SHELLTYPE" = "bash" ]; then
-    if [ -f "$HOME/.bashrc" ]; then
-      DETECTED_PROFILE="$HOME/.bashrc"
-    elif [ -f "$HOME/.bash_profile" ]; then
-      DETECTED_PROFILE="$HOME/.bash_profile"
-    fi
-  elif [ "$SHELLTYPE" = "zsh" ]; then
-    DETECTED_PROFILE="$HOME/.zshrc"
-  fi
-
-  if [ -z "$DETECTED_PROFILE" ]; then
-    if [ -f "$HOME/.profile" ]; then
-      DETECTED_PROFILE="$HOME/.profile"
-    elif [ -f "$HOME/.bashrc" ]; then
-      DETECTED_PROFILE="$HOME/.bashrc"
-    elif [ -f "$HOME/.bash_profile" ]; then
-      DETECTED_PROFILE="$HOME/.bash_profile"
-    elif [ -f "$HOME/.zshrc" ]; then
-      DETECTED_PROFILE="$HOME/.zshrc"
-    fi
-  fi
-
-  if [ ! -z "$DETECTED_PROFILE" ]; then
-    echo "$DETECTED_PROFILE"
-  fi
-}
-
-_initconf() {
-  _initpath
-  if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
-    echo "#Account configurations:
-#Here are the supported macros, uncomment them to make them take effect.
-#ACCOUNT_EMAIL=aaa@aaa.com  # the account email used to register account.
-
-#STAGE=1 # Use the staging api
-#FORCE=1 # Force to issue cert
-#DEBUG=1 # Debug mode
-
-#dns api
-#######################
-#Cloudflare:
-#api key
-#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
-#account email
-#CF_Email="xxxx@sss.com"
-
-#######################
-#Dnspod.cn:
-#api key id
-#DP_Id="1234"
-#api key
-#DP_Key="sADDsdasdgdsf"
-
-#######################
-#Cloudxns.com:
-#CX_Key="1234"
-#
-#CX_Secret="sADDsdasdgdsf"
-
-    " > $ACCOUNT_CONF_PATH
-  fi
-}
-
-install() {
-  _initpath
-  
-  #check if there is sudo installed, AND if the current user is a sudoer.
-  if command -v sudo > /dev/null ; then
-    if [ "$(sudo -n uptime 2>&1|grep "load"|wc -l)" != "0" ] ; then
-      SUDO=sudo
-    fi
-  fi
-  
-  if command -v yum > /dev/null ; then
-   YUM="1"
-   INSTALL="$SUDO yum install -y "
-  elif command -v apt-get > /dev/null ; then
-   INSTALL="$SUDO apt-get install -y "
-  fi
-
-  if ! command -v "curl" > /dev/null ; then
-    _err "Please install curl first."
-    _err "$INSTALL curl"
-    return 1
-  fi
-  
-  if ! command -v "crontab" > /dev/null ; then
-    _err "Please install crontab first."
-    if [ "$YUM" ] ; then
-      _err "$INSTALL crontabs"
-    else
-      _err "$INSTALL crontab"
-    fi
-    return 1
-  fi
-  
-  if ! command -v "openssl" > /dev/null ; then
-    _err "Please install openssl first."
-    _err "$INSTALL openssl"
-    return 1
-  fi
-
-  _info "Installing to $LE_WORKING_DIR"
-
-  _info "Installed to $LE_WORKING_DIR/le.sh" 
-  cp le.sh $LE_WORKING_DIR/
-  chmod +x $LE_WORKING_DIR/le.sh
-
-  _profile="$(_detect_profile)"
-  if [ "$_profile" ] ; then
-    _debug "Found profile: $_profile"
-    
-    echo "LE_WORKING_DIR=$LE_WORKING_DIR
-alias le=\"$LE_WORKING_DIR/le.sh\"
-alias le.sh=\"$LE_WORKING_DIR/le.sh\"
-    " > "$LE_WORKING_DIR/le.env"
-    
-    _setopt "$_profile" "source \"$LE_WORKING_DIR/le.env\""
-    _info "OK, Close and reopen your terminal to start using le"
-  else
-    _info "No profile is found, you will need to go into $LE_WORKING_DIR to use le.sh"
-  fi
-
-  mkdir -p $LE_WORKING_DIR/dnsapi
-  cp  dnsapi/* $LE_WORKING_DIR/dnsapi/
-  
-  #to keep compatible mv the .acc file to .key file 
-  if [ -f "$LE_WORKING_DIR/account.acc" ] ; then
-    mv "$LE_WORKING_DIR/account.acc" "$LE_WORKING_DIR/account.key"
-  fi
-  
-  installcronjob
-  
-  if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
-    _initconf
-  fi
-  _info OK
-}
-
-uninstall() {
-  uninstallcronjob
-  _initpath
-
-  _profile="$(_detect_profile)"
-  if [ "$_profile" ] ; then
-    sed -i /le.env/d  "$_profile"
-  fi
-
-  rm -f $LE_WORKING_DIR/le.sh
-  _info "The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself."
-
-}
-
-cron() {
-  renewAll
-}
-
-version() {
-  _info "$PROJECT"
-  _info "v$VER"
-}
-
-showhelp() {
-  version
-  echo "Usage: le.sh  [command] ...[args]....
-Avalible commands:
-
-install:
-  Install le.sh to your system.
-issue:
-  Issue a cert.
-installcert:
-  Install the issued cert to apache/nginx or any other server.
-renew:
-  Renew a cert.
-renewAll:
-  Renew all the certs.
-uninstall:
-  Uninstall le.sh, and uninstall the cron job.
-version:
-  Show version info.
-installcronjob:
-  Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
-uninstallcronjob:
-  Uninstall the cron job. The 'uninstall' command can do this automatically.
-createAccountKey:
-  Create an account private key, professional use.
-createDomainKey:
-  Create an domain private key, professional use.
-createCSR:
-  Create CSR , professional use.
-  "
-}
-
-
-if [ -z "$1" ] ; then
-  showhelp
-else
-  "$@"
-fi
+#!/usr/bin/env bash
+VER=1.1.8
+PROJECT="https://github.com/Neilpang/le"
+
+DEFAULT_CA="https://acme-v01.api.letsencrypt.org"
+DEFAULT_AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf"
+
+STAGE_CA="https://acme-staging.api.letsencrypt.org"
+
+VTYPE_HTTP="http-01"
+VTYPE_DNS="dns-01"
+
+if [ -z "$AGREEMENT" ] ; then
+  AGREEMENT="$DEFAULT_AGREEMENT"
+fi
+
+_debug() {
+
+  if [ -z "$DEBUG" ] ; then
+    return
+  fi
+  
+  if [ -z "$2" ] ; then
+    echo $1
+  else
+    echo "$1"="$2"
+  fi
+}
+
+_info() {
+  if [ -z "$2" ] ; then
+    echo "$1"
+  else
+    echo "$1"="$2"
+  fi
+}
+
+_err() {
+  if [ -z "$2" ] ; then
+    echo "$1" >&2
+  else
+    echo "$1"="$2" >&2
+  fi
+  return 1
+}
+
+_h2b() {
+  hex=$(cat)
+  i=1
+  j=2
+  while [ '1' ] ; do
+    h=$(printf $hex | cut -c $i-$j)
+    if [ -z "$h" ] ; then
+      break;
+    fi
+    printf "\x$h"
+    let "i+=2"
+    let "j+=2"
+  done
+}
+
+_base64() {
+  openssl base64 -e | tr -d '\n'
+}
+
+#domain [2048]  
+createAccountKey() {
+  _info "Creating account key"
+  if [ -z "$1" ] ; then
+    echo Usage: createAccountKey account-domain  [2048]
+    return
+  fi
+  
+  account=$1
+  length=$2
+  
+  if [[ "$length" == "ec-"* ]] ; then
+    length=2048
+  fi
+  
+  if [ -z "$2" ] ; then
+    _info "Use default length 2048"
+    length=2048
+  fi
+  _initpath
+  
+  if [ -f "$ACCOUNT_KEY_PATH" ] ; then
+    _info "Account key exists, skip"
+    return
+  else
+    #generate account key
+    openssl genrsa $length > "$ACCOUNT_KEY_PATH"
+  fi
+
+}
+
+#domain length
+createDomainKey() {
+  _info "Creating domain key"
+  if [ -z "$1" ] ; then
+    echo Usage: createDomainKey domain  [2048]
+    return
+  fi
+  
+  domain=$1
+  length=$2
+  isec=""
+  if [[ "$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
+  
+  if [ ! -f "$CERT_KEY_PATH" ] || ( [ "$FORCE" ] && ! [ "$IS_RENEW" ] ); then 
+    #generate account key
+    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
+    if [ "$IS_RENEW" ] ; then
+      _info "Domain key exists, skip"
+      return 0
+    else
+      _err "Domain key exists, do you want to overwrite the key?"
+      _err "Set FORCE=1, and try again."
+      return 1
+    fi
+  fi
+
+}
+
+# domain  domainlist
+createCSR() {
+  _info "Creating csr"
+  if [ -z "$1" ] ; then
+    echo Usage: $0 domain  [domainlist]
+    return
+  fi
+  domain=$1
+  _initpath $domain
+  
+  domainlist=$2
+  
+  if [ -f "$CSR_PATH" ]  && [ "$IS_RENEW" ] && ! [ "$FORCE" ]; then
+    _info "CSR exists, skip"
+    return
+  fi
+  
+  if [ -z "$domainlist" ] ; then
+    #single domain
+    _info "Single domain" $domain
+    openssl req -new -sha256 -key "$CERT_KEY_PATH" -subj "/CN=$domain" > "$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
+
+}
+
+_b64() {
+  __n=$(cat)
+  echo $__n | tr '/+' '_-' | tr -d '= '
+}
+
+_time2str() {
+  #BSD
+  if date -u -d@$1 2>/dev/null ; then
+    return
+  fi
+  
+  #Linux
+  if date -u -r $1 2>/dev/null ; then
+    return
+  fi
+  
+}
+
+_send_signed_request() {
+  url=$1
+  payload=$2
+  needbase64=$3
+  
+  _debug url $url
+  _debug payload "$payload"
+  
+  CURL_HEADER="$LE_WORKING_DIR/curl.header"
+  dp="$LE_WORKING_DIR/curl.dump"
+  CURL="curl --silent --dump-header $CURL_HEADER "
+  if [ "$DEBUG" ] ; then
+    CURL="$CURL --trace-ascii $dp "
+  fi
+  payload64=$(echo -n $payload | _base64 | _b64)
+  _debug payload64 $payload64
+  
+  nonceurl="$API/directory"
+  nonce="$($CURL -I $nonceurl | grep -o "^Replay-Nonce:.*$" | tr -d "\r\n" | cut -d ' ' -f 2)"
+
+  _debug nonce "$nonce"
+  
+  protected="$(printf "$HEADERPLACE" | sed "s/NONCE/$nonce/" )"
+  _debug protected "$protected"
+  
+  protected64="$(printf "$protected" | _base64 | _b64)"
+  _debug protected64 "$protected64"
+  
+  sig=$(echo -n "$protected64.$payload64" |  openssl   dgst   -sha256  -sign  $ACCOUNT_KEY_PATH | _base64 | _b64)
+  _debug sig "$sig"
+  
+  body="{\"header\": $HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
+  _debug body "$body"
+  
+  if [ "$needbase64" ] ; then
+    response="$($CURL -X POST --data "$body" $url | _base64)"
+  else
+    response="$($CURL -X POST --data "$body" $url)"
+  fi
+
+  responseHeaders="$(cat $CURL_HEADER)"
+  
+  _debug responseHeaders "$responseHeaders"
+  _debug response  "$response"
+  code="$(grep ^HTTP $CURL_HEADER | tail -1 | cut -d " " -f 2 | tr -d "\r\n" )"
+  _debug code $code
+
+}
+
+_get() {
+  url="$1"
+  _debug url $url
+  response="$(curl --silent $url)"
+  ret=$?
+  _debug response  "$response"
+  code="$(echo $response | grep -o '"status":[0-9]\+' | cut -d : -f 2)"
+  _debug code $code
+  return $ret
+}
+
+#setopt "file"  "opt"  "="  "value" [";"]
+_setopt() {
+  __conf="$1"
+  __opt="$2"
+  __sep="$3"
+  __val="$4"
+  __end="$5"
+  if [ -z "$__opt" ] ; then 
+    echo usage: _setopt  '"file"  "opt"  "="  "value" [";"]'
+    return
+  fi
+  if [ ! -f "$__conf" ] ; then
+    touch "$__conf"
+  fi
+
+  if grep -H -n "^$__opt$__sep" "$__conf" > /dev/null ; then
+    _debug OK
+    if [[ "$__val" == *"&"* ]] ; then
+      __val="$(echo $__val | sed 's/&/\\&/g')"
+    fi
+    text="$(cat $__conf)"
+    printf "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
+
+  elif grep -H -n "^#$__opt$__sep" "$__conf" > /dev/null ; then
+    if [[ "$__val" == *"&"* ]] ; then
+      __val="$(echo $__val | sed 's/&/\\&/g')"
+    fi
+    text="$(cat $__conf)"
+    printf "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
+
+  else
+    _debug APP
+    echo "$__opt$__sep$__val$__end" >> "$__conf"
+  fi
+  _debug "$(grep -H -n "^$__opt$__sep" $__conf)"
+}
+
+#_savedomainconf   key  value
+#save to domain.conf
+_savedomainconf() {
+  key="$1"
+  value="$2"
+  if [ "$DOMAIN_CONF" ] ; then
+    _setopt $DOMAIN_CONF "$key" "=" "$value"
+  else
+    _err "DOMAIN_CONF is empty, can not save $key=$value"
+  fi
+}
+
+#_saveaccountconf  key  value
+_saveaccountconf() {
+  key="$1"
+  value="$2"
+  if [ "$ACCOUNT_CONF_PATH" ] ; then
+    _setopt $ACCOUNT_CONF_PATH "$key" "=" "$value"
+  else
+    _err "ACCOUNT_CONF_PATH is empty, can not save $key=$value"
+  fi
+}
+
+_startserver() {
+  content="$1"
+  _NC="nc -q 1"
+  if nc -h 2>&1 | grep "nmap.org/ncat" >/dev/null ; then
+    _NC="nc"
+  fi
+#  while true ; do
+    if [ "$DEBUG" ] ; then
+      echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -l -p $Le_HTTPPort -vv
+    else
+      echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -l -p $Le_HTTPPort > /dev/null
+    fi
+#  done
+}
+
+_stopserver() {
+  pid="$1"
+
+}
+
+_initpath() {
+
+  if [ -z "$LE_WORKING_DIR" ]; then
+    LE_WORKING_DIR=$HOME/.le
+  fi
+  
+  if [ -z "$ACCOUNT_CONF_PATH" ] ; then
+    ACCOUNT_CONF_PATH="$LE_WORKING_DIR/account.conf"
+  fi
+  
+  if [ -f "$ACCOUNT_CONF_PATH" ] ; then
+    source "$ACCOUNT_CONF_PATH"
+  fi
+
+  if [ -z "$API" ] ; then
+    if [ -z "$STAGE" ] ; then
+      API="$DEFAULT_CA"
+    else
+      API="$STAGE_CA"
+      _info "Using stage api:$API"
+    fi  
+  fi
+  
+  if [ -z "$ACME_DIR" ] ; then
+    ACME_DIR="/home/.acme"
+  fi
+  
+  if [ -z "$APACHE_CONF_BACKUP_DIR" ] ; then
+    APACHE_CONF_BACKUP_DIR="$LE_WORKING_DIR/"
+  fi
+  
+  domain="$1"
+  mkdir -p "$LE_WORKING_DIR"
+  
+  if [ -z "$ACCOUNT_KEY_PATH" ] ; then
+    ACCOUNT_KEY_PATH="$LE_WORKING_DIR/account.key"
+  fi
+
+  if [ -z "$domain" ] ; then
+    return 0
+  fi
+  
+  domainhome="$LE_WORKING_DIR/$domain"
+  mkdir -p "$domainhome"
+
+  if [ -z "$DOMAIN_PATH" ] ; then
+    DOMAIN_PATH="$domainhome"
+  fi
+  if [ -z "$DOMAIN_CONF" ] ; then
+    DOMAIN_CONF="$domainhome/$Le_Domain.conf"
+  fi
+  
+  if [ -z "$DOMAIN_SSL_CONF" ] ; then
+    DOMAIN_SSL_CONF="$domainhome/$Le_Domain.ssl.conf"
+  fi
+  
+  if [ -z "$CSR_PATH" ] ; then
+    CSR_PATH="$domainhome/$domain.csr"
+  fi
+  if [ -z "$CERT_KEY_PATH" ] ; then 
+    CERT_KEY_PATH="$domainhome/$domain.key"
+  fi
+  if [ -z "$CERT_PATH" ] ; then
+    CERT_PATH="$domainhome/$domain.cer"
+  fi
+  if [ -z "$CA_CERT_PATH" ] ; then
+    CA_CERT_PATH="$domainhome/ca.cer"
+  fi
+
+}
+
+
+_apachePath() {
+  httpdroot="$(apachectl -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"' )"
+  httpdconfname="$(apachectl -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | tr -d '"' )"
+  httpdconf="$httpdroot/$httpdconfname"
+  if [ ! -f $httpdconf ] ; then
+    _err "Apache Config file not found" $httpdconf
+    return 1
+  fi
+  return 0
+}
+
+_restoreApache() {
+  if [ -z "$usingApache" ] ; then
+    return 0
+  fi
+  _initpath
+  if ! _apachePath ; then
+    return 1
+  fi
+  
+  if [ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ] ; then
+    _debug "No config file to restore."
+    return 0
+  fi
+  
+  cp -p "$APACHE_CONF_BACKUP_DIR/$httpdconfname" "$httpdconf"
+  if ! apachectl  -t ; then
+    _err "Sorry, restore apache config error, please contact me."
+    return 1;
+  fi
+  rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
+  return 0  
+}
+
+_setApache() {
+  _initpath
+  if ! _apachePath ; then
+    return 1
+  fi
+
+  #backup the conf
+  _debug "Backup apache config file" $httpdconf
+  cp -p $httpdconf $APACHE_CONF_BACKUP_DIR/
+  _info "JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
+  _info "In case there is an error that can not be restored automatically, you may try restore it yourself."
+  _info "The backup file will be deleted on sucess, just forget it."
+  
+  #add alias
+  echo "
+Alias /.well-known/acme-challenge  $ACME_DIR
+
+<Directory $ACME_DIR >
+Require all granted
+</Directory>
+  " >> $httpdconf
+  
+  if ! apachectl  -t ; then
+    _err "Sorry, apache config error, please contact me."
+    _restoreApache
+    return 1;
+  fi
+  
+  if [ ! -d "$ACME_DIR" ] ; then
+    mkdir -p "$ACME_DIR"
+    chmod 755 "$ACME_DIR"
+  fi
+  
+  if ! apachectl  graceful ; then
+    _err "Sorry, apachectl  graceful error, please contact me."
+    _restoreApache
+    return 1;
+  fi
+  usingApache="1"
+  return 0
+}
+
+_clearup () {
+  _stopserver $serverproc
+  serverproc=""
+  _restoreApache
+}
+
+# webroot  removelevel tokenfile
+_clearupwebbroot() {
+  __webroot="$1"
+  if [ -z "$__webroot" ] ; then
+    _debug "no webroot specified, skip"
+    return 0
+  fi
+  
+  if [ "$2" == '1' ] ; then
+    _debug "remove $__webroot/.well-known"
+    rm -rf "$__webroot/.well-known"
+  elif [ "$2" == '2' ] ; then
+    _debug "remove $__webroot/.well-known/acme-challenge"
+    rm -rf "$__webroot/.well-known/acme-challenge"
+  elif [ "$2" == '3' ] ; then
+    _debug "remove $__webroot/.well-known/acme-challenge/$3"
+    rm -rf "$__webroot/.well-known/acme-challenge/$3"
+  else
+    _info "Skip for removelevel:$2"
+  fi
+  
+  return 0
+
+}
+
+issue() {
+  if [ -z "$2" ] ; then
+    _err "Usage: le  issue  webroot|no|apache|dns   a.com  [www.a.com,b.com,c.com]|no   [key-length]|no"
+    return 1
+  fi
+  Le_Webroot="$1"
+  Le_Domain="$2"
+  Le_Alt="$3"
+  Le_Keylength="$4"
+  Le_RealCertPath="$5"
+  Le_RealKeyPath="$6"
+  Le_RealCACertPath="$7"
+  Le_ReloadCmd="$8"
+
+  
+  _initpath $Le_Domain
+  
+  if [ -f "$DOMAIN_CONF" ] ; then
+    Le_NextRenewTime=$(grep "^Le_NextRenewTime=" "$DOMAIN_CONF" | cut -d '=' -f 2)
+    if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then 
+      _info "Skip, Next renewal time is: $(grep "^Le_NextRenewTimeStr" "$DOMAIN_CONF" | cut -d '=' -f 2)"
+      return 2
+    fi
+  fi
+  
+  if [ "$Le_Alt" == "no" ] ; then
+    Le_Alt=""
+  fi
+  if [ "$Le_Keylength" == "no" ] ; then
+    Le_Keylength=""
+  fi
+  if [ "$Le_RealCertPath" == "no" ] ; then
+    Le_RealCertPath=""
+  fi
+  if [ "$Le_RealKeyPath" == "no" ] ; then
+    Le_RealKeyPath=""
+  fi
+  if [ "$Le_RealCACertPath" == "no" ] ; then
+    Le_RealCACertPath=""
+  fi
+  if [ "$Le_ReloadCmd" == "no" ] ; then
+    Le_ReloadCmd=""
+  fi
+  
+  _setopt "$DOMAIN_CONF"  "Le_Domain"             "="  "$Le_Domain"
+  _setopt "$DOMAIN_CONF"  "Le_Alt"                "="  "$Le_Alt"
+  _setopt "$DOMAIN_CONF"  "Le_Webroot"            "="  "$Le_Webroot"
+  _setopt "$DOMAIN_CONF"  "Le_Keylength"          "="  "$Le_Keylength"
+  _setopt "$DOMAIN_CONF"  "Le_RealCertPath"       "="  "\"$Le_RealCertPath\""
+  _setopt "$DOMAIN_CONF"  "Le_RealCACertPath"     "="  "\"$Le_RealCACertPath\""
+  _setopt "$DOMAIN_CONF"  "Le_RealKeyPath"        "="  "\"$Le_RealKeyPath\""
+  _setopt "$DOMAIN_CONF"  "Le_ReloadCmd"          "="  "\"$Le_ReloadCmd\""
+  
+  if [ "$Le_Webroot" == "no" ] ; then
+    _info "Standalone mode."
+    if ! command -v "nc" > /dev/null ; then
+      _err "Please install netcat(nc) tools first."
+      return 1
+    fi
+    
+    if [ -z "$Le_HTTPPort" ] ; then
+      Le_HTTPPort=80
+    fi
+    _setopt "$DOMAIN_CONF"  "Le_HTTPPort"             "="  "$Le_HTTPPort"
+    
+    netprc="$(ss -ntpl | grep :$Le_HTTPPort" ")"
+    if [ "$netprc" ] ; then
+      _err "$netprc"
+      _err "tcp port $Le_HTTPPort is already used by $(echo "$netprc" | cut -d :  -f 4)"
+      _err "Please stop it first"
+      return 1
+    fi
+  fi
+  
+  if [ "$Le_Webroot" == "apache" ] ; then
+    if ! _setApache ; then
+      _err "set up apache error. Report error to me."
+      return 1
+    fi
+    wellknown_path="$ACME_DIR"
+  else
+    usingApache=""
+  fi
+  
+  createAccountKey $Le_Domain $Le_Keylength
+  
+  if ! createDomainKey $Le_Domain $Le_Keylength ; then 
+    _err "Create domain key error."
+    return 1
+  fi
+  
+  if ! createCSR  $Le_Domain  $Le_Alt ; then
+    _err "Create CSR error."
+    return 1
+  fi
+
+  pub_exp=$(openssl rsa -in $ACCOUNT_KEY_PATH  -noout -text | grep "^publicExponent:"| cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1)
+  if [ "${#pub_exp}" == "5" ] ; then
+    pub_exp=0$pub_exp
+  fi
+  _debug pub_exp "$pub_exp"
+  
+  e=$(echo $pub_exp | _h2b | _base64)
+  _debug e "$e"
+  
+  modulus=$(openssl rsa -in $ACCOUNT_KEY_PATH -modulus -noout | cut -d '=' -f 2 )
+  n=$(echo $modulus| _h2b | _base64 | _b64 )
+
+  jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
+  
+  HEADER='{"alg": "RS256", "jwk": '$jwk'}'
+  HEADERPLACE='{"nonce": "NONCE", "alg": "RS256", "jwk": '$jwk'}'
+  _debug HEADER "$HEADER"
+  
+  accountkey_json=$(echo -n "$jwk" |  tr -d ' ' )
+  thumbprint=$(echo -n "$accountkey_json" | openssl dgst -sha256 -binary | _base64 | _b64)
+  
+  
+  _info "Registering account"
+  regjson='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}'
+  if [ "$ACCOUNT_EMAIL" ] ; then
+    regjson='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}'
+  fi  
+  _send_signed_request   "$API/acme/new-reg"  "$regjson"
+  
+  if [ "$code" == "" ] || [ "$code" == '201' ] ; then
+    _info "Registered"
+    echo $response > $LE_WORKING_DIR/account.json
+  elif [ "$code" == '409' ] ; then
+    _info "Already registered"
+  else
+    _err "Register account Error."
+    _clearup
+    return 1
+  fi
+  
+  vtype="$VTYPE_HTTP"
+  if [[ "$Le_Webroot" == "dns"* ]] ; then
+    vtype="$VTYPE_DNS"
+  fi
+  
+  vlist="$Le_Vlist"
+  # verify each domain
+  _info "Verify each domain"
+  sep='#'
+  if [ -z "$vlist" ] ; then
+    alldomains=$(echo "$Le_Domain,$Le_Alt" |  tr ',' ' ' )
+    for d in $alldomains   
+    do  
+      _info "Getting token for domain" $d
+      _send_signed_request "$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$d\"}}"
+      if [ ! -z "$code" ] && [ ! "$code" == '201' ] ; then
+        _err "new-authz error: $response"
+        _clearup
+        return 1
+      fi
+
+      entry="$(printf $response | egrep -o  '{[^{]*"type":"'$vtype'"[^}]*')"
+      _debug entry "$entry"
+
+      token="$(printf "$entry" | egrep -o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
+      _debug token $token
+      
+      uri="$(printf "$entry" | egrep -o '"uri":"[^"]*'| cut -d : -f 2,3 | tr -d '"' )"
+      _debug uri $uri
+      
+      keyauthorization="$token.$thumbprint"
+      _debug keyauthorization "$keyauthorization"
+
+      dvlist="$d$sep$keyauthorization$sep$uri"
+      _debug dvlist "$dvlist"
+      
+      vlist="$vlist$dvlist,"
+
+    done
+
+    #add entry
+    dnsadded=""
+    ventries=$(echo "$vlist" |  tr ',' ' ' )
+    for ventry in $ventries
+    do
+      d=$(echo $ventry | cut -d $sep -f 1)
+      keyauthorization=$(echo $ventry | cut -d $sep -f 2)
+
+      if [ "$vtype" == "$VTYPE_DNS" ] ; then
+        dnsadded='0'
+        txtdomain="_acme-challenge.$d"
+        _debug txtdomain "$txtdomain"
+        txt="$(echo -e -n $keyauthorization | openssl dgst -sha256 -binary | _base64 | _b64)"
+        _debug txt "$txt"
+        #dns
+        #1. check use api
+        d_api=""
+        if [ -f "$LE_WORKING_DIR/$d/$Le_Webroot" ] ; then
+          d_api="$LE_WORKING_DIR/$d/$Le_Webroot"
+        elif [ -f "$LE_WORKING_DIR/$d/$Le_Webroot.sh" ] ; then
+          d_api="$LE_WORKING_DIR/$d/$Le_Webroot.sh"
+        elif [ -f "$LE_WORKING_DIR/$Le_Webroot" ] ; then
+          d_api="$LE_WORKING_DIR/$Le_Webroot"
+        elif [ -f "$LE_WORKING_DIR/$Le_Webroot.sh" ] ; then
+          d_api="$LE_WORKING_DIR/$Le_Webroot.sh"
+        elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot" ] ; then
+          d_api="$LE_WORKING_DIR/dnsapi/$Le_Webroot"
+        elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh" ] ; then
+          d_api="$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh"
+        fi
+        _debug d_api "$d_api"
+        
+        if [ "$d_api" ]; then
+          _info "Found domain api file: $d_api"
+        else
+          _err "Add the following TXT record:"
+          _err "Domain: $txtdomain"
+          _err "TXT value: $txt"
+          _err "Please be aware that you prepend _acme-challenge. before your domain"
+          _err "so the resulting subdomain will be: $txtdomain"
+          continue
+        fi
+
+        if ! source $d_api ; then
+          _err "Load file $d_api error. Please check your api file and try again."
+          return 1
+        fi
+        
+        addcommand="$Le_Webroot-add"
+        if ! command -v $addcommand ; then 
+          _err "It seems that your api file is not correct, it must have a function named: $Le_Webroot"
+          return 1
+        fi
+        
+        if ! $addcommand $txtdomain $txt ; then
+          _err "Error add txt for domain:$txtdomain"
+          return 1
+        fi
+        dnsadded='1'
+      fi
+    done
+
+    if [ "$dnsadded" == '0' ] ; then
+      _setopt "$DOMAIN_CONF"  "Le_Vlist" "=" "\"$vlist\""
+      _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit."
+      _err "Please add the TXT records to the domains, and retry again."
+      return 1
+    fi
+    
+  fi
+  
+  if [ "$dnsadded" == '1' ] ; then
+    _info "Sleep 60 seconds for the txt records to take effect"
+    sleep 60
+  fi
+  
+  _debug "ok, let's start to verify"
+  ventries=$(echo "$vlist" |  tr ',' ' ' )
+  for ventry in $ventries
+  do
+    d=$(echo $ventry | cut -d $sep -f 1)
+    keyauthorization=$(echo $ventry | cut -d $sep -f 2)
+    uri=$(echo $ventry | cut -d $sep -f 3)
+    _info "Verifying:$d"
+    _debug "d" "$d"
+    _debug "keyauthorization" "$keyauthorization"
+    _debug "uri" "$uri"
+    removelevel=""
+    token=""
+    if [ "$vtype" == "$VTYPE_HTTP" ] ; then
+      if [ "$Le_Webroot" == "no" ] ; then
+        _info "Standalone mode server"
+        _startserver "$keyauthorization" &
+        serverproc="$!"
+        sleep 2
+        _debug serverproc $serverproc
+      else
+        if [ -z "$wellknown_path" ] ; then
+          wellknown_path="$Le_Webroot/.well-known/acme-challenge"
+        fi
+        _debug wellknown_path "$wellknown_path"
+        
+        if [ ! -d "$Le_Webroot/.well-known" ] ; then 
+          removelevel='1'
+        elif [ ! -d "$Le_Webroot/.well-known/acme-challenge" ] ; then 
+          removelevel='2'
+        else
+          removelevel='3'
+        fi
+        
+        token="$(echo -e -n "$keyauthorization" | cut -d '.' -f 1)"
+        _debug "writing token:$token to $wellknown_path/$token"
+
+        mkdir -p "$wellknown_path"
+        echo -n "$keyauthorization" > "$wellknown_path/$token"
+
+        webroot_owner=$(stat -c '%U:%G' $Le_Webroot)
+        _debug "Changing owner/group of .well-known to $webroot_owner"
+        chown -R $webroot_owner "$Le_Webroot/.well-known"
+        
+      fi
+    fi
+    
+    _send_signed_request $uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"
+    
+    if [ ! -z "$code" ] && [ ! "$code" == '202' ] ; then
+      _err "$d:Challenge error: $resource"
+      _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
+      _clearup
+      return 1
+    fi
+    
+    while [ "1" ] ; do
+      _debug "sleep 5 secs to verify"
+      sleep 5
+      _debug "checking"
+      
+      if ! _get $uri ; then
+        _err "$d:Verify error:$resource"
+        _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
+        _clearup
+        return 1
+      fi
+      
+      status=$(echo $response | egrep -o  '"status":"[^"]+"' | cut -d : -f 2 | tr -d '"')
+      if [ "$status" == "valid" ] ; then
+        _info "Success"
+        _stopserver $serverproc
+        serverproc=""
+        _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
+        break;
+      fi
+      
+      if [ "$status" == "invalid" ] ; then
+         error=$(echo $response | egrep -o '"error":{[^}]*}' | grep -o '"detail":"[^"]*"' | cut -d '"' -f 4)
+        _err "$d:Verify error:$error"
+        _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
+        _clearup
+        return 1;
+      fi
+      
+      if [ "$status" == "pending" ] ; then
+        _info "Pending"
+      else
+        _err "$d:Verify error:$response" 
+        _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
+        _clearup
+        return 1
+      fi
+      
+    done
+    
+  done
+
+  _clearup
+  _info "Verify finished, start to sign."
+  der="$(openssl req  -in $CSR_PATH -outform DER | _base64 | _b64)"
+  _send_signed_request "$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64"
+  
+  
+  Le_LinkCert="$(grep -i -o '^Location.*$' $CURL_HEADER | tr -d "\r\n" | cut -d " " -f 2)"
+  _setopt "$DOMAIN_CONF"  "Le_LinkCert"           "="  "$Le_LinkCert"
+
+  if [ "$Le_LinkCert" ] ; then
+    echo -----BEGIN CERTIFICATE----- > "$CERT_PATH"
+    curl --silent "$Le_LinkCert" | openssl base64 -e  >> "$CERT_PATH"
+    echo -----END CERTIFICATE-----  >> "$CERT_PATH"
+    _info "Cert success."
+    cat "$CERT_PATH"
+    
+    _info "Your cert is in $CERT_PATH"
+  fi
+  
+
+  if [ -z "$Le_LinkCert" ] ; then
+    response="$(echo $response | openssl base64 -d -A)"
+    _err "Sign failed: $(echo "$response" | grep -o  '"detail":"[^"]*"')"
+    return 1
+  fi
+  
+  _setopt "$DOMAIN_CONF"  'Le_Vlist' '=' "\"\""
+  
+  Le_LinkIssuer=$(grep -i '^Link' $CURL_HEADER | cut -d " " -f 2| cut -d ';' -f 1 | tr -d '<>' )
+  _setopt "$DOMAIN_CONF"  "Le_LinkIssuer"         "="  "$Le_LinkIssuer"
+  
+  if [ "$Le_LinkIssuer" ] ; then
+    echo -----BEGIN CERTIFICATE----- > "$CA_CERT_PATH"
+    curl --silent "$Le_LinkIssuer" | openssl base64 -e  >> "$CA_CERT_PATH"
+    echo -----END CERTIFICATE-----  >> "$CA_CERT_PATH"
+    _info "The intermediate CA cert is in $CA_CERT_PATH"
+  fi
+  
+  Le_CertCreateTime=$(date -u "+%s")
+  _setopt "$DOMAIN_CONF"  "Le_CertCreateTime"     "="  "$Le_CertCreateTime"
+  
+  Le_CertCreateTimeStr=$(date -u )
+  _setopt "$DOMAIN_CONF"  "Le_CertCreateTimeStr"  "="  "\"$Le_CertCreateTimeStr\""
+  
+  if [ ! "$Le_RenewalDays" ] ; then
+    Le_RenewalDays=80
+  fi
+  
+  _setopt "$DOMAIN_CONF"  "Le_RenewalDays"      "="  "$Le_RenewalDays"
+  
+  let "Le_NextRenewTime=Le_CertCreateTime+Le_RenewalDays*24*60*60"
+  _setopt "$DOMAIN_CONF"  "Le_NextRenewTime"      "="  "$Le_NextRenewTime"
+  
+  Le_NextRenewTimeStr=$( _time2str $Le_NextRenewTime )
+  _setopt "$DOMAIN_CONF"  "Le_NextRenewTimeStr"      "="  "\"$Le_NextRenewTimeStr\""
+
+
+  installcert $Le_Domain  "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd"
+
+}
+
+renew() {
+  Le_Domain="$1"
+  if [ -z "$Le_Domain" ] ; then
+    _err "Usage: $0  domain.com"
+    return 1
+  fi
+
+  _initpath $Le_Domain
+
+  if [ ! -f "$DOMAIN_CONF" ] ; then
+    _info "$Le_Domain is not a issued domain, skip."
+    return 0;
+  fi
+  
+  source "$DOMAIN_CONF"
+  if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then 
+    _info "Skip, Next renewal time is: $Le_NextRenewTimeStr"
+    return 2
+  fi
+  
+  IS_RENEW="1"
+  issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd"
+  local res=$?
+  IS_RENEW=""
+
+  return $res
+}
+
+renewAll() {
+  _initpath
+  _info "renewAll"
+  
+  for d in $(ls -F $LE_WORKING_DIR | grep [^.].*[.].*/$ ) ; do
+    d=$(echo $d | cut -d '/' -f 1)
+    _info "renew $d"
+    
+    Le_LinkCert=""
+    Le_Domain=""
+    Le_Alt=""
+    Le_Webroot=""
+    Le_Keylength=""
+    Le_LinkIssuer=""
+
+    Le_CertCreateTime=""
+    Le_CertCreateTimeStr=""
+    Le_RenewalDays=""
+    Le_NextRenewTime=""
+    Le_NextRenewTimeStr=""
+
+    Le_RealCertPath=""
+    Le_RealKeyPath=""
+    
+    Le_RealCACertPath=""
+
+    Le_ReloadCmd=""
+    
+    DOMAIN_PATH=""
+    DOMAIN_CONF=""
+    DOMAIN_SSL_CONF=""
+    CSR_PATH=""
+    CERT_KEY_PATH=""
+    CERT_PATH=""
+    CA_CERT_PATH=""
+    ACCOUNT_KEY_PATH=""
+    
+    wellknown_path=""
+    
+    renew "$d"  
+  done
+  
+}
+
+installcert() {
+  Le_Domain="$1"
+  if [ -z "$Le_Domain" ] ; then
+    _err "Usage: $0  domain.com  [cert-file-path]|no  [key-file-path]|no  [ca-cert-file-path]|no   [reloadCmd]|no"
+    return 1
+  fi
+
+  Le_RealCertPath="$2"
+  Le_RealKeyPath="$3"
+  Le_RealCACertPath="$4"
+  Le_ReloadCmd="$5"
+
+  _initpath $Le_Domain
+
+  _setopt "$DOMAIN_CONF"  "Le_RealCertPath"       "="  "\"$Le_RealCertPath\""
+  _setopt "$DOMAIN_CONF"  "Le_RealCACertPath"     "="  "\"$Le_RealCACertPath\""
+  _setopt "$DOMAIN_CONF"  "Le_RealKeyPath"        "="  "\"$Le_RealKeyPath\""
+  _setopt "$DOMAIN_CONF"  "Le_ReloadCmd"          "="  "\"$Le_ReloadCmd\""
+  
+  if [ "$Le_RealCertPath" ] ; then
+    if [ -f "$Le_RealCertPath" ] ; then
+      cp -p "$Le_RealCertPath" "$Le_RealCertPath".bak
+    fi
+    cat "$CERT_PATH" > "$Le_RealCertPath"
+  fi
+  
+  if [ "$Le_RealCACertPath" ] ; then
+    if [ -f "$Le_RealCACertPath" ] ; then
+      cp -p "$Le_RealCACertPath" "$Le_RealCACertPath".bak
+    fi
+    if [ "$Le_RealCACertPath" == "$Le_RealCertPath" ] ; then
+      echo "" >> "$Le_RealCACertPath"
+      cat "$CA_CERT_PATH" >> "$Le_RealCACertPath"
+    else
+      cat "$CA_CERT_PATH" > "$Le_RealCACertPath"
+    fi
+  fi
+
+
+  if [ "$Le_RealKeyPath" ] ; then
+    if [ -f "$Le_RealKeyPath" ] ; then
+      cp -p "$Le_RealKeyPath" "$Le_RealKeyPath".bak
+    fi
+    cat "$CERT_KEY_PATH" > "$Le_RealKeyPath"
+  fi
+
+  if [ "$Le_ReloadCmd" ] ; then
+    _info "Run Le_ReloadCmd: $Le_ReloadCmd"
+    (cd "$DOMAIN_PATH" && eval "$Le_ReloadCmd")
+  fi
+
+}
+
+installcronjob() {
+  _initpath
+  _info "Installing cron job"
+  if ! crontab -l | grep 'le.sh cron' ; then 
+    if [ -f "$LE_WORKING_DIR/le.sh" ] ; then
+      lesh="\"$LE_WORKING_DIR\"/le.sh"
+    else
+      _err "Can not install cronjob, le.sh not found."
+      return 1
+    fi
+    crontab -l | { cat; echo "0 0 * * * LE_WORKING_DIR=\"$LE_WORKING_DIR\" $lesh cron > /dev/null"; } | crontab -
+  fi
+  return 0
+}
+
+uninstallcronjob() {
+  _info "Removing cron job"
+  cr="$(crontab -l | grep 'le.sh cron')"
+  if [ "$cr" ] ; then 
+    crontab -l | sed "/le.sh cron/d" | crontab -
+    LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 6 | cut -d '=' -f 2 | tr -d '"')"
+    _info LE_WORKING_DIR "$LE_WORKING_DIR"
+  fi 
+  _initpath
+  
+}
+
+
+# Detect profile file if not specified as environment variable
+_detect_profile() {
+  if [ -n "$PROFILE" -a -f "$PROFILE" ]; then
+    echo "$PROFILE"
+    return
+  fi
+
+  local DETECTED_PROFILE
+  DETECTED_PROFILE=''
+  local SHELLTYPE
+  SHELLTYPE="$(basename "/$SHELL")"
+
+  if [ "$SHELLTYPE" = "bash" ]; then
+    if [ -f "$HOME/.bashrc" ]; then
+      DETECTED_PROFILE="$HOME/.bashrc"
+    elif [ -f "$HOME/.bash_profile" ]; then
+      DETECTED_PROFILE="$HOME/.bash_profile"
+    fi
+  elif [ "$SHELLTYPE" = "zsh" ]; then
+    DETECTED_PROFILE="$HOME/.zshrc"
+  fi
+
+  if [ -z "$DETECTED_PROFILE" ]; then
+    if [ -f "$HOME/.profile" ]; then
+      DETECTED_PROFILE="$HOME/.profile"
+    elif [ -f "$HOME/.bashrc" ]; then
+      DETECTED_PROFILE="$HOME/.bashrc"
+    elif [ -f "$HOME/.bash_profile" ]; then
+      DETECTED_PROFILE="$HOME/.bash_profile"
+    elif [ -f "$HOME/.zshrc" ]; then
+      DETECTED_PROFILE="$HOME/.zshrc"
+    fi
+  fi
+
+  if [ ! -z "$DETECTED_PROFILE" ]; then
+    echo "$DETECTED_PROFILE"
+  fi
+}
+
+_initconf() {
+  _initpath
+  if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
+    echo "#Account configurations:
+#Here are the supported macros, uncomment them to make them take effect.
+#ACCOUNT_EMAIL=aaa@aaa.com  # the account email used to register account.
+
+#STAGE=1 # Use the staging api
+#FORCE=1 # Force to issue cert
+#DEBUG=1 # Debug mode
+
+#dns api
+#######################
+#Cloudflare:
+#api key
+#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#account email
+#CF_Email="xxxx@sss.com"
+
+#######################
+#Dnspod.cn:
+#api key id
+#DP_Id="1234"
+#api key
+#DP_Key="sADDsdasdgdsf"
+
+#######################
+#Cloudxns.com:
+#CX_Key="1234"
+#
+#CX_Secret="sADDsdasdgdsf"
+
+    " > $ACCOUNT_CONF_PATH
+  fi
+}
+
+install() {
+  _initpath
+  
+  #check if there is sudo installed, AND if the current user is a sudoer.
+  if command -v sudo > /dev/null ; then
+    if [ "$(sudo -n uptime 2>&1|grep "load"|wc -l)" != "0" ] ; then
+      SUDO=sudo
+    fi
+  fi
+  
+  if command -v yum > /dev/null ; then
+   YUM="1"
+   INSTALL="$SUDO yum install -y "
+  elif command -v apt-get > /dev/null ; then
+   INSTALL="$SUDO apt-get install -y "
+  fi
+
+  if ! command -v "curl" > /dev/null ; then
+    _err "Please install curl first."
+    _err "$INSTALL curl"
+    return 1
+  fi
+  
+  if ! command -v "crontab" > /dev/null ; then
+    _err "Please install crontab first."
+    if [ "$YUM" ] ; then
+      _err "$INSTALL crontabs"
+    else
+      _err "$INSTALL crontab"
+    fi
+    return 1
+  fi
+  
+  if ! command -v "openssl" > /dev/null ; then
+    _err "Please install openssl first."
+    _err "$INSTALL openssl"
+    return 1
+  fi
+
+  _info "Installing to $LE_WORKING_DIR"
+
+  _info "Installed to $LE_WORKING_DIR/le.sh" 
+  cp le.sh $LE_WORKING_DIR/
+  chmod +x $LE_WORKING_DIR/le.sh
+
+  _profile="$(_detect_profile)"
+  if [ "$_profile" ] ; then
+    _debug "Found profile: $_profile"
+    
+    echo "LE_WORKING_DIR=$LE_WORKING_DIR
+alias le=\"$LE_WORKING_DIR/le.sh\"
+alias le.sh=\"$LE_WORKING_DIR/le.sh\"
+    " > "$LE_WORKING_DIR/le.env"
+    
+    _setopt "$_profile" "source \"$LE_WORKING_DIR/le.env\""
+    _info "OK, Close and reopen your terminal to start using le"
+  else
+    _info "No profile is found, you will need to go into $LE_WORKING_DIR to use le.sh"
+  fi
+
+  mkdir -p $LE_WORKING_DIR/dnsapi
+  cp  dnsapi/* $LE_WORKING_DIR/dnsapi/
+  
+  #to keep compatible mv the .acc file to .key file 
+  if [ -f "$LE_WORKING_DIR/account.acc" ] ; then
+    mv "$LE_WORKING_DIR/account.acc" "$LE_WORKING_DIR/account.key"
+  fi
+  
+  installcronjob
+  
+  if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
+    _initconf
+  fi
+  _info OK
+}
+
+uninstall() {
+  uninstallcronjob
+  _initpath
+
+  _profile="$(_detect_profile)"
+  if [ "$_profile" ] ; then
+    sed -i /le.env/d  "$_profile"
+  fi
+
+  rm -f $LE_WORKING_DIR/le.sh
+  _info "The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself."
+
+}
+
+cron() {
+  renewAll
+}
+
+version() {
+  _info "$PROJECT"
+  _info "v$VER"
+}
+
+showhelp() {
+  version
+  echo "Usage: le.sh  [command] ...[args]....
+Avalible commands:
+
+install:
+  Install le.sh to your system.
+issue:
+  Issue a cert.
+installcert:
+  Install the issued cert to apache/nginx or any other server.
+renew:
+  Renew a cert.
+renewAll:
+  Renew all the certs.
+uninstall:
+  Uninstall le.sh, and uninstall the cron job.
+version:
+  Show version info.
+installcronjob:
+  Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
+uninstallcronjob:
+  Uninstall the cron job. The 'uninstall' command can do this automatically.
+createAccountKey:
+  Create an account private key, professional use.
+createDomainKey:
+  Create an domain private key, professional use.
+createCSR:
+  Create CSR , professional use.
+  "
+}
+
+
+if [ -z "$1" ] ; then
+  showhelp
+else
+  "$@"
+fi