使用stdin.write()使用Python自动生成stdin

时间:2014-04-23 21:39:05

标签: python subprocess

我正在尝试自动生成自签名SSL证书。这是我的代码:

#!/usr/bin/env python   
import subprocess

pass_phrase = 'example'
common_name = 'example.com'
webmaster_email = 'webmaster@example.com'

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() 

当我运行它时,它仍然提示我输入PEM密码,然后返回此错误:

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

它应该输入上面的密码而不是提示我做任何事情。任何想法出了什么问题?

PS。我知道pexpect。请不要向我提出建议。

编辑:经过进一步调查,我已经弄明白了。如果您没有指定-nodes,私钥将被加密。因此,OpenSSL将立即提示输入PEM密码。这意味着我的stdin.write()的顺序搞砸了。我想替代方法是使用-nodes并稍后加密私钥。

2 个答案:

答案 0 :(得分:3)

您的代码中存在多个错误,例如,没有新行发送到子进程。

主要问题是openssl期望直接来自终端的密码短语(如Python中的getpass.getpass())。请参阅Why not just use a pipe (popen())?中的第一个原因:

  

首先,应用程序可以绕过stdout并直接打印到它   控制TTY。像SSH这样的东西会在它要求时执行此操作   密码这就是您无法重定向密码提示的原因   因为它不通过stdout或stderr。

提供伪tty的

pexpect在这种情况下可以正常工作:

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

pass_phrase = "dummy pass Phr6se"
common_name = "example.com"
email = "username@example.com"
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)

另一种方法是尝试使用-passin选项来指示openssl从不同的源(stdin,文件,管道,envvar,命令行)获取密码短语。我不知道它是否适用于openssl req命令。

答案 1 :(得分:1)

两个问题:

  1. 您没有按照预期的顺序向它提供预期的数据。在某些时候,它期待一个国家代码而你正在给它一些其他数据。
  2. 文件对象的write()方法不会自动插入换行符。您需要在字符串中添加"\n"或在要输入程序的每行输入后将write()单独的"\n"字符串添加出来。例如:proc.stdin.write(pass_phrase + "\n")