Running Openssl from a bash script on windows - Subject does not start with '/'

In my script I have:

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

Running this on Windows in Git Bash 3.1 gives:

Subject does not start with '/'.

Tried escaping the subj like so: -subj \"/C=UK/ST=someplace/L=Provo/O=Achme/CN=${FQDN}\"

Still doesn't work. Any ideas?


This issue is specific to MinGW/MSYS which is commonly used as part of the Git for Windows package.

The solution is to pass the -subj argument with leading // (double forward slashes) and then use \ (backslashes) to separate the key/value pairs. Like this:

"//O=Org\CN=Name"

This will then be magically passed to openssl in the expected form:

"/O=Org/CN=Name"

So to answer the specific question, you should change the -subj line in your script to the following.

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

That should be all you need.

What is this magic?

For those curious about exactly what is going on here, I can explain this mystery. The reason is that MSYS reasonably assumes that arguments containing slashes are actually paths. And when those arguments are passed to an executable that haven't been compiled specifically for MSYS (like openssl in this case) then it will convert POSIX paths to Win32 paths. The rules for this conversion are quite complex as MSYS tries its best to cover most common scenarios for interoperability. This also explains why using openssl from a windows command prompt (cmd.exe) works fine, because no magical conversions are made.

You can test the conversion like this.

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

We can't use the echo executable that comes with MSYS since it was compiled for MSYS, instead we'll use the echo builtin in cmd. Notice that since cmd switches starts with / (common for windows commands) we need to handle that with double slashes. As we can see in the output the argument was expanded to a windows path and it becomes clear why openssl does indeed claim that Subject does not start with '/'..

Let's see some more conversions.

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

Double slashes makes MSYS believe the argument is a windows style switch which results in stripping a / only (no path conversion). You would think that with this we could just use slashes to add more key/value pairs. Let's try that.

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

Suddenly the double slashes in the start isn't stripped down. This is because now, with a slash following the initial double slashes, MSYS thinks we are referencing a UNC path (e.g. //server/path). If this was passed to openssl it would skip the first key/value saying Subject Attribute /O has no known NID, skipped.

Here is the relevant rule from the MinGW wiki explaining this behavior:

  • An argument starting with 2 or more / is considered an escaped Windows style switch and will be passed with the leading / removed and all \ changed to /.
    • Except that if there is a / following the leading block of /, the argument is considered to be a UNC path and the leading / is not removed.

In this rule we can see the method we could use to create the argument we want. Since all \ that follows in an argument starting with // will be converted to plain /. Let's try that out.

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

And as we can see it does work.

Hope this demystifies the magic a little bit.