【问题标题】:Automate stdin with Python using stdin.write()使用 Python 使用 stdin.write() 自动化标准输入
【发布时间】:2014-04-23 21:39:04
【问题描述】:

我正在尝试自动设置生成自签名 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 并稍后加密私钥。

【问题讨论】:

  • 它看起来像是在寻找一个国家名称,但你没有给它。
  • 为什么:“我知道。请不要向我推荐它。”?
  • 因为根据我在 stackoverflow 上的经验,用户倾向于用一般的回答来回复,希望这会为他们赢得积分,而不是真诚地回答问题。

标签: python subprocess


【解决方案1】:

您的代码中有几个错误,例如,没有向子进程发送换行符。

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

首先,应用程序可以绕过标准输出并直接打印到它的 控制 TTY。当它要求您提供时,像 SSH 之类的东西会这样做 密码。这就是您无法重定向密码提示的原因 因为它不经过stdout或stderr。

pexpect 提供伪 tty 在这种情况下可以正常工作:

#!/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 命令。

【讨论】:

  • 感谢您的帮助。我试图避免 pexpect,因为它不附带股票 Python。
  • pexpect is pure Python,如果您不希望您的用户单独安装它,您可以将它放在您的代码旁边(不推荐)
  • @PythonNoob 如果你不想使用 pexpect,你将不得不重新发明它的一部分。
【解决方案2】:

两个问题:

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

【讨论】:

  • OpenSSL 行为怪异,并在允许 stdin.write() 运行之前提示输入 PEM 密码,导致 stdin.write() 的顺序混乱。
  • @PythonNoob 可能是直接打开了控制PTY。
猜你喜欢
  • 2011-10-02
  • 1970-01-01
  • 2011-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-18
  • 1970-01-01
相关资源
最近更新 更多