私のスクリプトには次のものがあります:
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}"
Git Bash 3.1のWindowsでこれを実行すると、以下が得られます。
Subject does not start with '/'.
次のようにsubjをエスケープしようとしました:-subj\"/ C = UK/ST = someplace/L = Provo/O = Achme/CN = $ {FQDN} \"
それでも動作しません。何か案は?
この問題は、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は相互運用性の最も一般的なシナリオをカバーしようと最善を尽くしているため、この変換のルールは非常に複雑です。これは、魔法の変換が行われないため、Windowsコマンドプロンプト(cmd.exe
)からopenssl
を使用することで問題なく動作する理由も説明しています。
このように変換をテストできます。
$ cmd //c echo "/CN=Name"
"C:/Program Files (x86)/Git/CN=Name"
MSYS用にコンパイルされているため、MSYSに付属するecho
実行可能ファイルは使用できません。代わりに、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パスと見なされ、先頭の/は削除されません。
このルールでは、必要な引数を作成するために使用できるメソッドを確認できます。 \
で始まる引数に続くすべての//
は、単純な/
に変換されるためです。それを試してみましょう。
$ cmd //c echo "//O=Org\CN=Name"
/O=Org/CN=Name
そして、見てわかるように、それは機能します。
これが魔法を少し分かりやすくすることを願っています。
私は、これが使用中のOpenSSLバイナリに固有のものであると個人的に知りました。 msys2/mingw64を使用しているシステムでは、次の2つの異なる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を使用して作業するときに、OpenSSLバージョンに基づいて正しいバイナリを選択する次のbashコードの例を見つけました。
# 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のサイズに関する問題を解決するためにもこれを見つけました(どのフィールドにもmax_sizeを設定せず、まだ問題があったポリシーでカスタムopenssl.cnfを渡しました/mingw64/bin/openssl.exe
)を使用する場合。