我经常处理 SSL 证书、私钥、CSR 等。我有许多小型 bash 脚本可以自动执行我的任务。其中一个脚本使用 openssl 为 SSL 证书生成新的私钥和 CSR。该脚本实际上只有一行加上错误检查 - 以节省我的输入:
#!/usr/bin/env bash
if [[ $# -ne 1 ]]; then
echo "Format: $0 hostname"
echo " e.g. $0 www.example.com"
exit 2
fi
openssl req -new -newkey rsa:2048 -nodes -keyout "$1.key" -out "$1.csr" \
-subj "/C=GB/ST=County/L=City/O=My Company Ltd./CN=$1"
cat "$1.csr"
这一直运作良好。然而最近我需要越来越多的带有 SAN 的证书,所以我修改了脚本,如下所示:
#!/usr/bin/env bash
if [[ $# -eq 0 ]]; then
echo "Format: $0 hostname"
echo " e.g. $0 www.example.com [SAN1 SAN2 ...]"
exit 2
fi
SANS=""
if [[ $# -gt 1 ]]; then
SANS=""
for san in "${@:2}"; do
SANS="${SANS}DNS:${san}, "
done
SANS="-addext \"subjectAltName = ${SANS%,*}\""
fi
openssl req -new -newkey rsa:2048 -nodes -keyout "$1.key" -out "$1.csr" \
-subj "/C=GB/ST=County/L=City/O=My Company Ltd./CN=$1" \
$SANS
cat "$1.csr"
现在脚本不再起作用,只需生成
req: Use -help for summary.
我修改了脚本的最后一点,如下所示:
CMD="openssl req -new -newkey rsa:2048 -nodes -keyout \"$1.key\" -out \"$1.csr\" \
-subj \"/C=GB/ST=County/L=City/O=My Company Ltd./CN=$1\" \
$SANS"
echo $CMD
$CMD
这使我可以在执行命令之前查看该命令。该命令是正确的,当我将此命令复制/粘贴到 bash 提示符中时,我确实得到了正确的结果。那么,是什么原因导致该命令在从脚本执行时失败呢?
答案1
你的问题是你的SANS
变量,它是一个字符串。您使用不带引号的变量,这意味着 shell 会将其拆分为空格(并对所有拆分部分应用文件名通配)。
而不是存储多个单独的字符串在一个字符串中,使用一个数组:
SANS=()
if [[ $# -gt 1 ]]; then
subjAltNames=("${@:2}")
IFS=,
SANS=(-addext "subjectAltName = ${subjAltNames[*]}")
fi
如果$#
大于 1,则给出SANS
一个数组,其中包含两个元素-addext
和字符串,subjectAltNames =
后跟来自命令行参数的以逗号分隔的字符串列表。逗号分隔列表是从另一个数组创建的,subjAltNames
通过将其用作,其元素与作为分隔符${subjAltNames[*]}
的第一个字符连接起来(这就是我们预先设置为逗号的原因)。$IFS
IFS
您稍后将使用SANS
as"${SANS[@]}"
来正确访问单独引用的元素的列表:
openssl req -new -newkey rsa:2048 -nodes -keyout "$1.key" -out "$1.csr" \
-subj "/C=GB/ST=County/L=City/O=My Company Ltd./CN=$1" \
"${SANS[@]}"
如果SANS
数组为空,则不会向参数列表的末尾提供空参数。