Ejecución de Openssl desde un script bash en Windows: el asunto no comienza con '/'

83

En mi guión tengo:

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

Ejecutar esto en Windows en Git Bash 3.1 da:

Subject does not start with '/'.

Intenté escapar del sujeto así: -subj \ "/ C = UK / ST = en algún lugar / L = Provo / O = Achme / CN = $ {FQDN} \"

Todavía no funciona. ¿Algunas ideas?

iss42
fuente
1
Primera pregunta estándar: ¿su archivo de script tiene terminaciones de línea estilo DOS / Windows (retorno de carro + salto de línea), o estilo Unix (solo salto de línea)? Intente imprimir el script con cat -vet /path/to/script, y vea si las líneas terminan con '^ M $' (estilo Windows) o simplemente '$' (estilo Unix).
Gordon Davisson
1
¿Este es un script bash? ¿Correr en qué entorno? ¿Qué set -vxse está ejecutando para esta línea al agregar al principio del script?
Etan Reisner
@EtanReisner set -vxes útil gracias! El entorno es Windows, Git bash 3.1. Con -vx , obtengo + 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.comque muestra la -subjcadena sin comillas . Pero no puedo averiguar cómo hacer esto en una forma citada del guión.
iss42
@GordonDavisson ¡gracias! el script tiene terminaciones de línea '^ M $'
iss42
1
Un argumento no citado en la -vxsalida no es sorprendente ni es un problema. Las comillas son para el análisis del shell, no para la ejecución del comando en sí. Esa salida me parece correcta. Los finales de línea de DOS generalmente no son una buena idea, pero no parecen haber causado ningún problema aquí (a menos que eliminarlos solucione el problema, en cuyo caso estoy un poco confundido por el mensaje de error).
Etan Reisner

Respuestas:

197

Este problema es específico de MinGW / MSYS, que se usa comúnmente como parte del paquete Git para Windows .

La solución es pasar el -subjargumento al principio //(barras diagonales dobles) y luego usar \(barras diagonales inversas) para separar los pares clave / valor. Me gusta esto:

"//O=Org\CN=Name"

Esto luego se pasará mágicamente a opensslen la forma esperada:

"/O=Org/CN=Name"

Entonces, para responder la pregunta específica, debe cambiar la -subjlínea en su secuencia de comandos a la siguiente.

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

Eso deberia ser todo lo que necesitas.

¿Qué es esta magia?

Para aquellos que tengan curiosidad por saber exactamente qué está pasando aquí, puedo explicar este misterio. La razón es que MSYS asume razonablemente que los argumentos que contienen barras son en realidad rutas. Y cuando esos argumentos se pasan a un ejecutable que no se ha compilado específicamente para MSYS (como opensslen este caso), convertirá las rutas POSIX en rutas Win32 . Las reglas para esta conversión son bastante complejas ya que MSYS hace todo lo posible para cubrir los escenarios más comunes de interoperabilidad. Esto también explica por qué el uso openssldesde un símbolo del sistema de Windows ( cmd.exe) funciona bien, porque no se realizan conversiones mágicas.

Puede probar la conversión de esta manera.

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

No podemos usar el echoejecutable que viene con MSYS ya que fue compilado para MSYS, en su lugar usaremos el echoincorporado cmd. Tenga en cuenta que, dado que los cmdinterruptores comienzan con /(común para los comandos de Windows), debemos manejar eso con barras dobles. Como podemos ver en la salida, el argumento se expandió a una ruta de Windows y queda claro por qué opensslafirma eso Subject does not start with '/'..

Veamos algunas conversiones más.

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

Las barras dobles hacen que MSYS crea que el argumento es un cambio de estilo de Windows que da como resultado la eliminación de un /solo (sin conversión de ruta). Pensaría que con esto podríamos usar barras diagonales para agregar más pares clave / valor. Probemos eso.

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

De repente, las barras dobles al principio no se reducen. Esto se debe a que ahora, con una barra después de las barras dobles iniciales, MSYS cree que estamos haciendo referencia a una ruta UNC (por ejemplo, // servidor / ruta). Si se le pasara opensslesto, se omitiría la primera clave / valor diciendo Subject Attribute /O has no known NID, skipped.

Aquí está la regla relevante de la wiki de MinGW que explica este comportamiento:

  • Un argumento que comienza con 2 o más / se considera un cambio de estilo de Windows de escape y se pasará con el / eliminado y todo \ cambiado a /.
    • Excepto que si hay un / después del bloque inicial de /, el argumento se considera una ruta UNC y el / inicial no se elimina.

En esta regla podemos ver el método que podríamos usar para crear el argumento que queremos. Dado \que todo lo que sigue en un argumento que comienza con //se convertirá a simple /. Probemos eso.

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

Y como podemos ver, funciona.

Espero que esto desmitifique un poco la magia.

Korroz
fuente
1
Gran explicación
Trebor
4
¿Qué pasa si utilizo el mismo bashscript para generar claves en el entorno Linux? ¿Cómo se interpretaría que las barras dobles y las barras invertidas iniciales en el medio de la línea?
Tomilov Anatoliy
3
@Orient Linux necesita las barras en la otra dirección, por lo que deberá detectar en qué tipo de sistema se está ejecutando; aquí hay una respuesta que usa una casedeclaración y uname -spara detectar el entorno, que luego puede usar con un ifpara usar el apropiado barras inclinadas - stackoverflow.com/questions/3466166/…
Tim Lewis
Completamente asombroso. Me atrapó con el mismo problema y me había olvidado por completo de la conversión de ruta de POSIX a Win32. Seguía pensando que lo estaba citando mal.
davewasthere
0

Personalmente, encontré que esto es específico para el binario OpenSSL en uso. En mi sistema que usa msys2 / mingw64, he notado que hay dos binarios OpenSSL diferentes, por ejemplo:

$ 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

Creo que el uso de /mingw64/bin/openssleso requiere usar un tema que comience con //, sin embargo, no estoy seguro si esto es específico para el paquete / compilación o la versión de OpenSSL, así que para estar seguro, la versión de cada binario está a continuación:

$ 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

Encontré el siguiente ejemplo de código bash para seleccionar el binario correcto según la versión de OpenSSL cuando utilizo msys / mingw para trabajar en mi máquina:

# 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

Además de solucionar problemas al pasar la cadena de asunto, también encontré esto para resolver problemas con el tamaño del DN (pasé un openssl.cnf personalizado con una política que no establecía un max_size para ninguno de los campos y que aún tenía problemas al usar /mingw64/bin/openssl.exe).

Rob Frey
fuente