在Windows上从bash脚本运行Openssl-主题不是以'/'开头


80

在我的脚本中,我有:

openssl req \
  -x509 \
  -new \
  -nodes \
  -key certs/ca/my-root-ca.key.pem \
  -days 3652 \
  -out certs/ca/my-root-ca.crt.pem \
  -subj "/C=GB/ST=someplace/L=Provo/O=Achme/CN=${FQDN}"

在Windows上的Git Bash 3.1中运行此命令可以:

Subject does not start with '/'.

尝试像这样转义主题:-subj \“ / C = UK / ST = someplace / L = Provo / O = Achme / CN = $ {FQDN} \”

仍然不起作用。有任何想法吗?


1
标准第一个问题:您的脚本文件是否具有DOS / Windows样式的行尾(回车+换行)或unix样式(仅换行)?尝试使用来打印脚本cat -vet /path/to/script,然后查看行是否以'^ M $'(Windows风格)或仅以'$'(unix风格)结尾。
戈登·戴维森

1
这是bash脚本吗?在什么环境下运行?set -vx正在为此行运行添加到脚本顶部的内容是什么?
Etan Reisner,2015年

@EtanReisnerset -vx非常有用,谢谢!环境是Windows,Git bash 3.1。使用-vx,我得到+ openssl req -x509 -new -nodes -key certs/ca/my-root-ca.key.pem -days 3652 -out certs/ca/my-root-ca.crt.pem -subj /C=GB/ST=someplace/L=Provo/O=Achme/CN=domain.com显示未引号的-subj字符串。但是我不知道如何从脚本中将其转换为带引号的形式。
iss42

@GordonDavisson谢谢!脚本的行尾带有'^ M $'
iss42

1
-vx输出中未引用的参数并不令人惊讶或存在问题。引号用于shell解析,而不是命令执行本身。该输出在我看来是正确的。DOS的行尾通常不是一个好主意,但是这里似乎并没有引起任何问题(除非删除它们可以解决问题,在这种情况下,我对错误消息会有些困惑)。
伊坦·赖斯纳

Answers:


194

此问题特定于MinGW / MSYS,后者通常用作Windows Git软件包的一部分。

解决方案是-subj用前导//(双正斜杠)传递参数,然后使用\(反斜杠)分隔键/值对。像这样:

"//O=Org\CN=Name"

然后,它将以openssl预期的形式神奇地传递给:

"/O=Org/CN=Name"

因此,要回答特定问题,应将-subj脚本中的行更改为以下内容。

-subj "//C=GB\ST=someplace\L=Provo\O=Achme\CN=${FQDN}"

那应该是您所需要的。

这是什么魔术?

对于那些对这里到底发生了什么感到好奇的人,我可以解释这个谜。原因是MSYS合理地假设包含斜杠的参数实际上是路径。而且,当这些参数传递给尚未专门为MSYS编译的可执行文件时(如openssl本例所示),它将把POSIX路径转换为Win32路径。这种转换的规则非常复杂,因为MSYS会尽力涵盖最常见的互操作性方案。这也解释了为什么openssl在Windows命令提示符(cmd.exe)中使用效果很好,因为没有进行神奇的转换。

您可以像这样测试转换。

$ cmd //c echo "/CN=Name"
"C:/Program Files (x86)/Git/CN=Name"

由于echoMSYS随附的可执行文件是为MSYS编译的,因此我们无法使用它,而是使用echo内置的cmd。请注意,由于cmd开关以/(对于Windows命令常见)开头,因此我们需要使用双斜杠来处理。正如我们在输出中看到的那样,该参数已扩展到Windows路径,并且很清楚为什么openssl确实声明了它Subject does not start with '/'.

让我们看看更多的转换。

$ cmd //c echo "//CN=Name"
/CN=Name

双斜杠使MSYS认为该参数是Windows样式的开关,导致/仅剥离(无路径转换)。您可能会认为,我们可以仅使用斜杠添加更多键/值对。让我们尝试一下。

$ cmd //c echo "//O=Org/CN=Name"
//O=Org/CN=Name

突然,开始时的双斜杠没有被删除。这是因为现在,在最初的双斜杠之后有一个斜杠,MSYS认为我们正在引用UNC路径(例如// server / path)。如果将其传递给openssl它,则会跳过第一个键/值“” Subject Attribute /O has no known NID, skipped

这是MinGW Wiki中解释此行为的相关规则:

  • 以2或更大的/开头的参数被认为是Windows样式的转义参数,并将以前导/删除且所有\更改为/传递。
    • 除非在/的前导块之后有一个/,否则该参数被视为UNC路径,并且不删除前导/。

在此规则中,我们可以看到可用于创建所需参数的方法。由于\以参数开头的所有后续内容//都将转换为plain /。让我们尝试一下。

$ cmd //c echo "//O=Org\CN=Name"
/O=Org/CN=Name

正如我们所见,它确实有效。

希望这能使魔术变得神秘。


1
很好的解释。
trebor

4
如果我bash在Linux环境中使用相同的-script来生成密钥怎么办?在行的中间如何解释前导双斜杠和反斜杠?
Tomilov Anatoliy

3
@Orient Linux需要反斜杠,因此您需要检测它正在运行的系统类型-这是一个使用case语句的答案并uname -s检测环境,然后可以将其与一起使用if以使用适当的系统斜线-stackoverflow.com/questions/3466166/…–
蒂姆·刘易斯

非常棒。我遇到了同样的问题,完全忘记了POSIX到Win32路径的转换。一直以为我在引用错误。
davewasthere

0

我个人发现这是特定于使用的OpenSSL二进制文件。在使用msys2 / mingw64的系统上,我注意到存在两个不同的OpenSSL二进制文件,例如:

$ whereis openssl; echo; which openssl
openssl: /usr/bin/openssl.exe /usr/lib/openssl /mingw64/bin/openssl.exe /usr/share/man/man1/openssl.1ssl.gz

/mingw64/bin/openssl

我相信这是/mingw64/bin/openssl需要使用以开头的主题的一种用法//,但是我不确定这是否特定于软件包/内部版本或OpenSSL的版本,因此可以肯定的是,每个二进制文件的版本如下:

$ while read -r _openSslBin; do printf "${_openSslBin}: "; ${_openSslBin} version; done < <(whereis openssl | egrep -o '[^ ]+?\.exe ')
/usr/bin/openssl.exe: OpenSSL 1.0.2p  14 Aug 2018
/mingw64/bin/openssl.exe: OpenSSL 1.1.1  11 Sep 2018

使用msys / mingw在我的机器上工作时,我发现以下bash代码示例可根据OpenSSL版本选择正确的二进制文件:

# determine openssl binary to use based on OS
# -------------------------------------------
_os="$(uname -s | awk 'BEGIN{FS="_"} {print $1}' | egrep -o '[A-Za-z]+')"
if [ "${_os,,}" = "mingw" ] || [ "${_os,,}" == "msys" ]; then
  while read -r _currentOpenSslBin; do
    if [[ "$(${_currentOpenSslBin}  version | awk '{print $2}')" =~ ^(1\.0\.[0-9].*|0\.\9\.8.*)$ ]]; then
      _openSslBin="${_currentOpenSslBin}"
    fi
  done < <(whereis openssl | egrep -o '\/[^ ]+?\.exe ' | egrep -v 'mingw')
  if [ -n "${_openSslBin}" ]; then
    printf "OpenSSL Binary: ${_openSslBin} (v. $(${_openSslBin}  version | awk '{print $2}'))\n"
  else
    printf "Unable to find compatible version of OpenSSL for use with '${_os}' OS, now exiting...\n"
    exit 1
  fi
else
  _openSslBin="openssl"
fi

# display selected openssl binary and it's version
# ------------------------------------------------
printf "${_openSslBin}: "; ${_openSslBin} version

除了解决传递主题字符串的问题外,我还发现这可以解决DN大小的问题(我通过一个自定义的openssl.cnf,该策略未为任何字段设置max_size并且仍然存在问题使用时/mingw64/bin/openssl.exe)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.