0

I've read that I can put bash parentheses in quotes when submitting the command from python. In this case, I'm trying to create key and csr for openssl using a command that works on the shell, but not when executed from python. I've tried several combinations, but keep running into additional issues.

The following python command fails:

import os

os.system('openssl req -nodes -newkey rsa:2048 -sha256 -keyout mynode.key -out mynode.csr -subj "/C=US/ST=Florida/L=St Petersburg/O=MyCompany/OU=MyOU/CN=mynode/emailAddress=administrators@my.com" -reqexts OPTS -config <(cat /etc/pki/tls/openssl.cnf <(printf "[OPTS]\  basicConstraints = CA:FALSE  subjectAltName = DNS:mynode,DNS:myf5  keyUsage = digitalSignature, keyEncipherment  extendedKeyUsage = serverAuth, clientAuth"))')

The underlying openssl command succeeds:

openssl req -nodes -newkey rsa:2048 -sha256 -keyout mynode.key -out mynode.csr -subj "/C=US/ST=Florida/L=St Petersburg/O=MyCompany/OU=MyOU/CN=mynode/emailAddress=administrators@my.com" -reqexts OPTS -config <(cat /etc/pki/tls/openssl.cnf <(printf "[OPTS]\  basicConstraints = CA:FALSE  subjectAltName = DNS:mynode,DNS:myf5  keyUsage = digitalSignature, keyEncipherment  extendedKeyUsage = serverAuth, clientAuth"))

This must be something simple, but the answer eludes me.

2
  • Python has (Open)SSL libraries, do not shell out to openssl, use the Python libraries for your "SSL" operations. In fact for certificate issuance you can used "M2Crypto" for example, see gitlab.com/m2crypto/m2crypto/blob/master/M2Crypto/X509.py#L1105 for a CSR Aug 30 '19 at 22:39
  • bash is able to run that second version, but the data it provides to OpenSSL is incorrectly formatted and does not actually produce the intended extensions. You need either actual (quoted) newlines or \n escapes interpreted by printf after [OPTS] and each extension item. Aug 31 '19 at 2:14
2

<(…) is process substitution, which exists in bash (and ksh and zsh) but not in plain sh. The system function invokes sh, not bash.

You can use a pipe instead of the process substitution onto openssl, and simplify the nested process substitution into a simple sequence of commands.

{ cat /etc/pki/tls/openssl.cnf;
  printf "[OPTS]\  basicConstraints = CA:FALSE  subjectAltName = DNS:mynode,DNS:myf5  keyUsage = digitalSignature, keyEncipherment  extendedKeyUsage = serverAuth, clientAuth";
} | openssl req -nodes -newkey rsa:2048 -sha256 -keyout mynode.key -out mynode.csr -subj "/C=US/ST=Florida/L=St Petersburg/O=MyCompany/OU=MyOU/CN=mynode/emailAddress=administrators@my.com" -reqexts OPTS -config -

(line breaks optional)

1
  • Note -config - only works in 1.1.0 up. (And the data copied from the Q is wrong because it doesn't have needed linebreaks.) Aug 31 '19 at 2:15
0

Thanks for the responses. I thought about it a bit and decided to look for a python-native way to do it. There was enough information at the python cryptography cheatsheet website and others that I was able to put together the python code to generate the CSR.

requires pyOpenSSL


from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend())

with open('mycert.key', 'wb') as f:
     f.write(key.private_bytes(
     encoding=serialization.Encoding.PEM,
     format=serialization.PrivateFormat.TraditionalOpenSSL,
     encryption_algorithm=serialization.NoEncryption()))

from OpenSSL import crypto

# load private key
ftype = crypto.FILETYPE_PEM
with open('mycert.key', 'rb') as f: key = f.read()
key = crypto.load_privatekey(ftype, key)
req    = crypto.X509Req()

alt_name  = [ b"DNS:mynode",
              b"DNS:myF5",
              b"email:administratore@email.com" ]
key_usage = [ b"Digital Signature",
              b"Key Encipherment" ]
key_usage = [ b"digitalSignature",
              b"keyEncipherment" ]
ext_key_usage = [ b"serverAuth",
                  b"clientAuth" ]

# country (countryName, C)
# state or province name (stateOrProvinceName, ST)
# locality (locality, L)
# organization (organizationName, O)
# organizational unit (organizationalUnitName, OU)
# common name (commonName, CN)

req.get_subject().C  = "US"
req.get_subject().ST = "Florida"
req.get_subject().L  = "St Petersburg"
req.get_subject().O  = "myCompany"
req.get_subject().OU = "MyOU"
req.get_subject().CN = "mynode"
req.add_extensions([
    crypto.X509Extension( b"basicConstraints",
                          False,
                          b"CA:FALSE"),
    crypto.X509Extension( b"keyUsage",
                          False,
                          b",".join(key_usage)),
    crypto.X509Extension( b"subjectAltName",
                          False,
                          b",".join(alt_name))
])

req.set_pubkey(key)
req.sign(key, "sha256")

csr = crypto.dump_certificate_request(ftype, req)
with open("mycert.csr", 'wb') as f: f.write(csr)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.