1

I am trying to automate the setup of generating self-signed SSL certificate. This is my code:

#!/usr/bin/env python   
import subprocess

pass_phrase = 'example'
common_name = 'example.com'
webmaster_email = '[email protected]'

proc = subprocess.Popen(['openssl', 'req', '-x509', '-newkey', 'rsa:2048', '-rand', '/dev/urandom', '-keyout', '/etc/pki/tls/private/server.key', '-out', '/etc/pki/tls/certs/server.crt', '-days', '180'], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)

for i in range(2):
    proc.stdin.write(pass_phrase)
for i in range(5):
    proc.stdin.write('.')
proc.stdin.write(common_name)
proc.stdin.write(webmaster_email)
proc.stdin.flush()

stdout, stderr = proc.communicate() 

When I run it, it still prompts me for the PEM passphrase, then returns this error:

Country Name (2 letter code) [XX]:weird input :-(
problems making Certificate Request

It should feed in the passphrase above and not prompt me for anything. Any ideas what's going wrong?

PS. I know about pexpect. Please don't suggest it to me.

Edit: Upon further investigation, I've figured it out. If you don't specify -nodes, the private key will be encrypted. So, OpenSSL will prompt for a PEM passphrase immediately. This means the order of my stdin.write() gets messed up. I guess the alternative is to use -nodes and encrypt the private key later.

3
  • It looks like it is looking for a Country Name, but you are not giving it one. Commented Apr 23, 2014 at 21:42
  • 1
    Why: "I know about <solution that might help me>. Please don't suggest it to me."? Commented Apr 23, 2014 at 23:44
  • Because from what I've experienced on stackoverflow, users tend to reply with generic responses hoping it will earn them points instead of trying to answer the question sincerely. Commented Apr 24, 2014 at 22:04

2 Answers 2

3

There are several errors in your code e.g., no newlines are sent to the child process.

The main issue is that openssl expects the pass phrase directly from the terminal (like getpass.getpass() in Python). See the first reason in Why not just use a pipe (popen())?:

First an application may bypass stdout and print directly to its controlling TTY. Something like SSH will do this when it asks you for a password. This is why you cannot redirect the password prompt because it does not go through stdout or stderr.

pexpect that provides pseudo-tty works fine in this case:

#!/usr/bin/env python
import sys
from pexpect import spawn, EOF

pass_phrase = "dummy pass Phr6se"
common_name = "example.com"
email = "[email protected]"
keyname, certname = 'server.key', 'server.crt'

cmd = 'openssl req -x509 -newkey rsa:2048 -rand /dev/urandom '.split()
cmd += ['-keyout', keyname, '-out', certname, '-days', '180']

child = spawn(cmd[0], cmd[1:], timeout=10)    
child.logfile_read = sys.stdout # show openssl output for debugging

for _ in range(2):
    child.expect('pass phrase:')
    child.sendline(pass_phrase)
for _ in range(5):
    child.sendline('.')
child.sendline(common_name)
child.sendline(email)
child.expect(EOF)
child.close()
sys.exit(child.status)

An alternative is to try to use -passin option to instruct openssl to get the pass phrase from a different source (stdin, a file, pipe, envvar, command-line). I don't know whether it works with openssl req command.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your help. I'm trying to avoid pexpect since it doesn't come with stock Python.
pexpect is pure Python, you could put it alongside your code (not recommended) if you don't want your user to install it separately
@PythonNoob If you don't want to use pexpect, you'll have to reinvent part of it.
1

Two problems:

  1. You are not giving it the data it expects in the order it expects. At some point it is expecting a country code and you are giving it some other data instead.
  2. The write() method of file objects does not automatically insert a newline. You need to add "\n" to your strings or write() separate "\n" strings out after each line of input you want to feed to the program. For example: proc.stdin.write(pass_phrase + "\n")

2 Comments

OpenSSL acts weird and prompts for PEM passphrase before allowing stdin.write() to run resulting in the order of stdin.write()'s to be messed up.
@PythonNoob It might be opening the controlling PTY directly.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.