Ausführen von Openssl über ein Bash-Skript unter Windows - Der Betreff beginnt nicht mit '/'

81

In meinem Skript habe ich:

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}"

Wenn Sie dies unter Windows in Git Bash 3.1 ausführen, erhalten Sie:

Subject does not start with '/'.

Versucht, dem Subjekt wie folgt zu entkommen: -subj "/ C = UK / ST = irgendwo / L = Provo / O = Achme / CN = $ {FQDN}"

Funktioniert immer noch nicht. Irgendwelche Ideen?

iss42
quelle
1
Standard erste Frage: Hat Ihre Skriptdatei Zeilenenden im DOS / Windows-Stil (Wagenrücklauf + Zeilenvorschub) oder Unix-Stil (nur Zeilenvorschub)? Versuchen Sie, das Skript mit zu drucken cat -vet /path/to/script, und prüfen Sie, ob die Zeilen mit '^ M $' (Windows-Stil) oder nur mit '$' (Unix-Stil) enden.
Gordon Davisson
1
Dies ist ein Bash-Skript? Unter welcher Umgebung laufen? Was bedeutet das Hinzufügen set -vxzum Anfang der Skriptshow für diese Zeile?
Etan Reisner
@EtanReisner set -vxist nützlich, danke! Umgebung ist Windows, Git Bash 3.1. Mit -vx erhalte ich + 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.comdie -subjZeichenfolge ohne Anführungszeichen . Aber ich kann nicht herausfinden, wie ich dies aus dem Skript in eine zitierte Form bringen kann.
iss42
@ GordonDavisson danke! Skript hat '^ M $' Zeilenenden
iss42
1
Ein nicht zitiertes Argument in der -vxAusgabe ist nicht überraschend oder ein Problem. Die Anführungszeichen beziehen sich auf die Shell-Analyse und nicht auf die Befehlsausführung selbst. Diese Ausgabe sieht für mich korrekt aus. DOS-Zeilenenden sind im Allgemeinen keine gute Idee, scheinen hier jedoch keine Probleme verursacht zu haben (es sei denn, das Entfernen behebt das Problem. In diesem Fall bin ich durch die Fehlermeldung etwas verwirrt).
Etan Reisner

Antworten:

195

Dieses Problem ist spezifisch für MinGW / MSYS, das üblicherweise als Teil des Git für Windows- Pakets verwendet wird.

Die Lösung besteht darin, das -subjArgument mit führenden //(doppelte Schrägstriche) zu übergeben und dann \die Schlüssel / Wert-Paare mit (umgekehrten Schrägstrichen) zu trennen. So was:

"//O=Org\CN=Name"

Dies wird dann auf magische Weise opensslin der erwarteten Form weitergegeben:

"/O=Org/CN=Name"

Um die spezifische Frage zu beantworten, sollten Sie die -subjZeile in Ihrem Skript wie folgt ändern .

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

Das sollte alles sein, was Sie brauchen.

Was ist das für eine Magie?

Für diejenigen, die genau wissen wollen, was hier vor sich geht, kann ich dieses Rätsel erklären. Der Grund dafür ist, dass MSYS vernünftigerweise davon ausgeht, dass Argumente, die Schrägstriche enthalten, tatsächlich Pfade sind. Wenn diese Argumente an eine ausführbare Datei übergeben werden, die nicht speziell für MSYS kompiliert wurde (wie opensslin diesem Fall), werden POSIX-Pfade in Win32-Pfade konvertiert . Die Regeln für diese Konvertierung sind recht komplex, da MSYS sein Bestes versucht, die gängigsten Szenarien für die Interoperabilität abzudecken. Dies erklärt auch, warum die Verwendung opensslan einer Windows-Eingabeaufforderung (cmd.exe ) gut funktioniert, da keine magischen Konvertierungen vorgenommen werden.

Sie können die Konvertierung folgendermaßen testen.

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

Wir können die echomit MSYS echogelieferte ausführbare Datei nicht verwenden , da sie für MSYS kompiliert wurde. Stattdessen verwenden wir die integrierte Datei cmd. Beachten Sie, dass wir , da cmdSchalter mit beginnen /(häufig für Windows-Befehle), dies mit doppelten Schrägstrichen behandeln müssen. Wie wir in der Ausgabe sehen können, wurde das Argument zu einem Windows-Pfad erweitert, und es wird klar, warum openssldies tatsächlich behauptet wird Subject does not start with '/'..

Sehen wir uns noch einige Conversions an.

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

Doppelte Schrägstriche lassen MSYS glauben, dass das Argument ein Windows-Stilschalter ist, der dazu führt, dass /nur ein Strip entfernt wird (keine Pfadkonvertierung). Sie würden denken, dass wir damit nur Schrägstriche verwenden könnten, um weitere Schlüssel / Wert-Paare hinzuzufügen. Lass es uns versuchen.

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

Plötzlich werden die doppelten Schrägstriche am Start nicht mehr reduziert. Dies liegt daran, dass MSYS jetzt mit einem Schrägstrich nach den anfänglichen doppelten Schrägstrichen denkt, dass wir auf einen UNC-Pfad verweisen (z. B. // Server / Pfad). Wenn dies an übergeben opensslwürde, würde es den ersten Schlüssel / Wert-Spruch überspringen Subject Attribute /O has no known NID, skipped.

Hier ist die relevante Regel aus dem MinGW-Wiki , die dieses Verhalten erklärt:

  • Ein Argument, das mit 2 oder mehr / beginnt, wird als Escape-Schalter im Windows-Stil betrachtet und mit dem führenden / entfernten und allen \ geändert in / übergeben.
    • Mit der Ausnahme, dass bei einem / nach dem führenden Block von / das Argument als UNC-Pfad betrachtet wird und das führende / nicht entfernt wird.

In dieser Regel sehen wir die Methode, mit der wir das gewünschte Argument erstellen können. Da alles \, was in einem Argument folgt, das mit beginnt, in //normal konvertiert wird /. Probieren wir das aus.

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

Und wie wir sehen können, funktioniert es.

Hoffe das entmystifiziert die Magie ein wenig.

Korroz
quelle
1
Tolle Erklärung.
Trebor
4
Was ist, wenn ich dasselbe bashSkript verwende, um Schlüssel in einer Linux-Umgebung zu generieren? Wie würde man interpretieren, dass führende doppelte Schrägstriche und Backslashes in der Mitte der Linie liegen?
Tomilov Anatoliy
3
@Orient Linux benötigt die Schrägstriche in die andere Richtung, damit Sie erkennen können, auf welchem ​​Systemtyp es ausgeführt wird. Hier ist eine Antwort, die eine caseAnweisung verwendet und uname -sdie Umgebung erkennt, die Sie dann mit einem verwenden können if, um die entsprechende zu verwenden Schrägstriche - stackoverflow.com/questions/3466166/…
Tim Lewis
Total genial. Ich hatte das gleiche Problem und hatte die Pfadkonvertierung von POSIX zu Win32 völlig vergessen. Ich dachte immer, ich zitiere es falsch.
Davewasthere
0

Ich persönlich fand dies spezifisch für die verwendete OpenSSL-Binärdatei. Auf meinem System mit msys2 / mingw64 habe ich festgestellt, dass zwei verschiedene OpenSSL-Binärdateien vorhanden sind, zum Beispiel:

$ 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

Ich glaube, um dies zu tun /mingw64/bin/openssl, muss ein Betreff verwendet werden, der mit beginnt. //Ich bin mir jedoch nicht sicher, ob dies für das Paket / den Build oder die Version von OpenSSL spezifisch ist. Um sicherzugehen, ist die Version jeder Binärdatei unten aufgeführt:

$ 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

Ich habe das folgende Beispiel für Bash-Code gefunden, um die richtige Binärdatei basierend auf der OpenSSL-Version auszuwählen, wenn msys / mingw für die Arbeit auf meinem Computer verwendet wird:

# 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

Zusätzlich zur Behebung von Problemen beim Übergeben der Betreffzeichenfolge habe ich festgestellt, dass dies Probleme mit der Größe des DN behebt (ich habe eine benutzerdefinierte openssl.cnf mit einer Richtlinie übergeben, die für keines der Felder eine max_size festgelegt hat und die immer noch Probleme hatte bei der Verwendung /mingw64/bin/openssl.exe).

Rob Frey
quelle