Uruchamianie Openssl ze skryptu bash w systemie Windows - temat nie zaczyna się od znaku „/”

83

W swoim skrypcie mam:

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

Uruchomienie tego w systemie Windows w Git Bash 3.1 daje:

Subject does not start with '/'.

Próbowano uciekać przed subj w następujący sposób: -subj \ "/ C = UK / ST = gdzieś / L = Provo / O = Achme / CN = $ {FQDN} \"

Nadal nie działa. Jakieś pomysły?

iss42
źródło
1
Standardowe pierwsze pytanie: czy twój plik skryptu ma zakończenia linii w stylu DOS / Windows (powrót karetki + znak nowej linii), czy w stylu unixowym (tylko przesunięcie wiersza)? Spróbuj wydrukować skrypt za pomocą cat -vet /path/to/scripti zobacz, czy wiersze kończą się na „^ M $” (w stylu Windows), czy po prostu na „$” (w stylu unix).
Gordon Davisson,
1
To jest skrypt bash? Uruchom w jakim środowisku? Co dodaje set -vxna początku pokazu skryptu jest uruchamiane dla tej linii?
Etan Reisner,
@EtanReisner set -vxjest przydatny dzięki! Środowisko to Windows, Git bash 3.1. Z -vx , otrzymuję, + 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.comktóry pokazuje -subjciąg znaków bez cudzysłowów . Ale nie mogę wymyślić, jak umieścić to w cytowanej formie ze scenariusza.
iss42
@GordonDavisson dzięki! skrypt ma końcówki linii „^ M $”
iss42
1
Niecytowany argument w -vxwyniku nie jest zaskakujący ani nie stanowi problemu. Cudzysłowy dotyczą analizy powłoki, a nie samego wykonania polecenia. Wydaje mi się, że ten wynik jest poprawny. Zakończenia linii DOS generalnie nie są dobrym pomysłem, ale wydaje się, że nie spowodowały tutaj żadnych problemów (chyba że ich usunięcie rozwiązuje problem, w którym to przypadku jestem trochę zdezorientowany komunikatem o błędzie).
Etan Reisner,

Odpowiedzi:

197

Ten problem jest specyficzny dla MinGW / MSYS, który jest powszechnie używany jako część pakietu Git for Windows .

Rozwiązaniem jest przekazanie -subjargumentu z początkiem //(podwójne ukośniki w przód), a następnie użycie \(ukośnika odwrotnego) do oddzielenia par klucz / wartość. Lubię to:

"//O=Org\CN=Name"

Zostanie to następnie magicznie przekazane opensslw oczekiwanej formie:

"/O=Org/CN=Name"

Aby odpowiedzieć na konkretne pytanie, należy zmienić -subjwiersz w skrypcie na następujący.

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

To powinno wystarczyć.

Co to za magia?

Dla ciekawych tego, co się tutaj dzieje, mogę wyjaśnić tę tajemnicę. Powodem jest to, że MSYS rozsądnie zakłada, że ​​argumenty zawierające ukośniki są w rzeczywistości ścieżkami. A kiedy te argumenty są przekazywane do pliku wykonywalnego, który nie został skompilowany specjalnie dla MSYS (tak jak opensslw tym przypadku), wówczas konwertuje on ścieżki POSIX na ścieżki Win32 . Zasady tej konwersji są dość złożone, ponieważ MSYS stara się jak najlepiej uwzględniać większość typowych scenariuszy współdziałania. To wyjaśnia również, dlaczego używanie opensslz wiersza poleceń systemu Windows ( cmd.exe) działa dobrze, ponieważ nie są wykonywane żadne magiczne konwersje.

Możesz przetestować konwersję w ten sposób.

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

Nie możemy użyć echopliku wykonywalnego, który jest dostarczany z MSYS, ponieważ został skompilowany dla MSYS, zamiast tego użyjemy echowbudowanego cmd. Zauważ, że ponieważ cmdprzełączniki zaczynają się od /(typowe dla poleceń systemu Windows), musimy obsługiwać to za pomocą podwójnych ukośników. Jak widać na wyjściu, argument został rozszerzony do ścieżki systemu Windows i staje się jasne, dlaczego opensslrzeczywiście tak twierdzi Subject does not start with '/'..

Zobaczmy więcej konwersji.

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

Podwójne ukośniki sprawiają, że MSYS uważa, że ​​argument jest przełącznikiem stylu systemu Windows, który powoduje usunięcie /tylko (bez konwersji ścieżki). Można by pomyśleć, że dzięki temu moglibyśmy po prostu użyć ukośników, aby dodać więcej par klucz / wartość. Spróbujmy tego.

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

Nagle podwójne cięcia na początku nie są usuwane. Dzieje się tak, ponieważ teraz, z ukośnikiem po początkowych podwójnych ukośnikach, MSYS uważa, że ​​odwołujemy się do ścieżki UNC (np. // serwer / ścieżka). Gdyby to zostało przekazane do openssltego, pomijałoby pierwszy klucz / wartość mówiącą Subject Attribute /O has no known NID, skipped.

Oto odpowiednia reguła z wiki MinGW wyjaśniająca to zachowanie:

  • Argument zaczynający się od 2 lub więcej / jest uważany za przełącznik stylu Windows ze znakami ucieczki i zostanie przekazany z początkiem / usuniętym, a wszystkie \ zmienione na /.
    • Z wyjątkiem tego, że jeśli występuje / po wiodącym bloku /, argument jest uważany za ścieżkę UNC, a wiodący / nie jest usuwany.

W tej regule widzimy metodę, której moglibyśmy użyć do stworzenia żądanego argumentu. Ponieważ wszystko, \co następuje w argumencie zaczynającym się od, //zostanie przekonwertowane na zwykły /. Wypróbujmy to.

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

I jak widzimy, to działa.

Mam nadzieję, że to trochę demistyfikuje magię.

Korroz
źródło
1
Świetne wyjaśnienie.
trebor
4
Co się stanie, jeśli bashużyję tego samego skryptu do generowania kluczy w środowisku linux? Jak można by zinterpretować, że wiodące podwójne ukośniki i odwrotne ukośniki na środku linii?
Tomilov Anatoliy
3
@Orient Linux potrzebuje ukośników w przeciwnym kierunku, więc musisz wykryć, na jakim typie systemu działa - oto odpowiedź, która używa caseinstrukcji i uname -sdo wykrywania środowiska, którego możesz następnie użyć w ifcelu użycia odpowiedniego slashy - stackoverflow.com/questions/3466166/…
Tim Lewis,
Fantastycznie. Zostałem złapany na tym samym problemie i całkowicie zapomniałem o konwersji ścieżki POSIX do Win32. Myślałem, że źle to zacytowałem.
davewasthere
0

Osobiście uważam, że jest to specyficzne dla używanego pliku binarnego OpenSSL. W moim systemie używającym msys2 / mingw64 zauważyłem, że obecne są dwa różne pliki binarne OpenSSL, na przykład:

$ 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

Uważam, że użycie /mingw64/bin/openssltego wymaga użycia tematu, który zaczyna się od //, jednak nie jestem pewien, czy jest to specyficzne dla pakietu / kompilacji lub wersji OpenSSL, więc dla pewności wersja każdego pliku binarnego jest poniżej:

$ 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

Znalazłem następujący przykład kodu bash, aby wybrać poprawny plik binarny na podstawie wersji OpenSSL podczas używania msys / mingw do pracy na moim komputerze:

# 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

Oprócz naprawienia problemów z przekazywaniem ciągu tematu znalazłem również to, aby rozwiązać problemy z rozmiarem nazwy wyróżniającej (przekazałem niestandardowy plik openssl.cnf z polityką, która nie ustawiała max_size dla żadnego z pól i która nadal miała problemy podczas używania /mingw64/bin/openssl.exe).

Rob Frey
źródło