Skip to content

Instantly share code, notes, and snippets.

@bohanyang
Last active September 14, 2022 13:06
Show Gist options
  • Save bohanyang/b34aff4ff880a56650e345718d551829 to your computer and use it in GitHub Desktop.
Save bohanyang/b34aff4ff880a56650e345718d551829 to your computer and use it in GitHub Desktop.
一键生成 CSR

一键生成 CSR

  • 极少见地支持 Windows 下的 Git Bash(MinGW/MSYS)
  • 支持 RSA 和 ECDSA
  • 支持使用已有私钥
  • 支持生成 PKCS #8 格式私钥(-----BEGIN PRIVATE KEY-----
  • 支持自定义 Subject DN(Distinguished Name,如 /C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=example.com
  • 支持不带 CN(Common Name)字段的 Subject DN
  • 支持空白 Subject DN
  • 支持 OCSP Must-Staple
  • ✨支持生成用于 IP 证书的 CSR

使用方法

# 下载并赋予执行权限后即可使用
curl -LO https://z.sh/csr.sh
chmod +x csr.sh

# 生成包含如下两个域名的 CSR
./csr.sh "*.example.com" "example.org"
# 生成的私钥文件是当前目录下的 _example_com.key

./csr.sh --key ssl/wildcard.key "*.example.com" "example.org"
# 生成的私钥文件是 ssl/wildcard.key
# 如果私钥文件存在,将直接使用,不再重新生成

指定密钥算法

# 默认使用 2048 位 RSA
# 位数紧跟在 --rsa 之后
./csr.sh "example.com"
./csr.sh --rsa "example.com"
./csr.sh --rsa2048 "example.com"
./csr.sh --rsa4096 "example.com"

# ECDSA 默认使用 NIST P-256 (prime256v1/secp256r1) 曲线
# 曲线名称紧跟在 --ec 之后
./csr.sh --ec "example.com"
./csr.sh --ecprime256v1 "example.com"
./csr.sh --ecsecp384r1 "example.com"
# 256 和 384 分别是 prime256v1 和 secp384r1 的别名
./csr.sh --ec256 "example.com"
./csr.sh --ec384 "example.com"

自定义 Subject DN

./csr.sh --subj "/O=CloudFlare, Inc./OU=CloudFlare Origin CA/CN=CloudFlare Origin Certificate" "example.com"
# 结果:/O=CloudFlare, Inc./OU=CloudFlare Origin CA/CN=CloudFlare Origin Certificate

# 默认自动添加第一个域名到 CN 字段
./csr.sh --subj "/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd" "*.example.com" "example.org"
# 结果:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=*.example.com

# 如果想取消自动添加的 CN 字段,可以使用 --no-add-cn
./csr.sh --subj "/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd" --no-add-cn "*.example.com" "example.org"
# 结果:/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd

Subject DN 中默认会有一个国家字段,其值为南极洲,用来充数(如 /C=AQ/CN=example.com)。可以这样去掉它:

./csr.sh --subj "" "example.com"
# 结果:/CN=example.com

# 结合 --no-add-cn 实现空白 Subject DN
./csr.sh --subj "" --no-add-cn "example.com"

生成 PKCS #8 私钥

./csr.sh --pkcs8 "example.com"

启用 OCSP Must-Staple

./csr.sh --must-staple "example.com"

生成 IP 证书

./csr.sh --ip 1.1.1.1 --ip 2606:4700:4700::1111 one.one.one.one

许可协议

公有领域 Unlicense

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
#!/usr/bin/env sh
# shellcheck shell=dash disable=SC2064
set -eu
_req() {
local csr
csr=$(mktemp)
openssl req -new -key "$key" -config "$cnf" -subj "$subj" -out "$csr"
openssl req -in "$csr" -text -verify -noout 1>&2
echo 1>&2
cat "$csr"
rm "$csr"
}
_rsa() {
[ -z "$bits" ] && bits=2048
if [ "$pkcs8" = true ]; then
openssl genpkey -algorithm RSA -pkeyopt "rsa_keygen_bits:$bits" -out "$key"
else
openssl genrsa -out "$key" "$bits"
fi
}
_ec() {
# https://tools.ietf.org/search/rfc4492#page-32
case $curve in
256|c|'')
curve=prime256v1
;;
384)
curve=secp384r1
;;
esac
if [ "$pkcs8" = true ]; then
openssl genpkey -algorithm EC -pkeyopt "ec_paramgen_curve:$curve" -out "$key"
else
openssl ecparam -name "$curve" -genkey -noout -out "$key"
fi
}
_san() {
[ -z "$cn" ] && cn=$1
if [ -z "$san" ]; then
san="$2:$1"
else
san="$san,$2:$1"
fi
}
_csr() {
export san=
alg=_rsa
bits=
curve=
cn=
key=
subj='/C=AQ'
add_cn=true
pkcs8=false
must_staple=false
while [ $# -gt 0 ]; do
case $1 in
--ip)
_san "$2" IP
shift
;;
--rsa*)
alg=_rsa
bits=${1#--rsa}
;;
--ec*)
alg=_ec
curve=${1#--ec}
;;
--key)
key=$2
shift
;;
--subj)
subj=$2
shift
;;
--no-add-cn)
add_cn=false
;;
--pkcs8)
pkcs8=true
;;
--must-staple)
must_staple=true
;;
-*|'')
echo "Unknown option \"$1\"." 1>&2
return 1
;;
*)
_san "$1" DNS
esac
shift
done
if [ -z "$cn" ] || [ -z "$san" ]; then
echo 'No domain name or IP address specified.' 1>&2
return 1
fi
if [ -z "$key" ] || [ ! -e "$key" ]; then
[ -z "$key" ] && key="./$(printf '%s' "${cn#\*}" | sed 's,\.,_,g').key"
"$alg"
fi
if [ -z "$subj" ]; then
if [ "$add_cn" = true ]; then
subj="/CN=$cn"
else
subj='/'
fi
elif [ "$add_cn" = true ] && [ "${subj#*/CN=}" = "$subj" ]; then
subj="$subj/CN=$cn"
fi
if [ "$(uname -o)" = Msys ] && [ "${subj#*\*}" = "$subj" ]; then
after_slash="${subj#/}"
[ "$after_slash" != "$subj" ] && subj="//$(printf '%s' "$after_slash" | sed 's,/,\\,g')"
fi
local cnf
cnf=$(mktemp)
cat << 'EOF' > "$cnf"
[ req ]
distinguished_name = req_distinguished_name
req_extensions = v3_req
[ req_distinguished_name ]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = $ENV::san
EOF
if [ "$must_staple" = true ]; then
echo '1.3.6.1.5.5.7.1.24 = DER:30:03:02:01:05' >> "$cnf"
fi
_req
rm "$cnf"
cat << EOF 1>&2
Private Key File: $key
EOF
}
_csr "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment