使用stdin.write()使用Python自动化stdin

使用stdin.write()使用Python自动化stdin,python,subprocess,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',

我正在尝试自动设置生成自签名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
它应该输入上面的密码短语,而不是提示我什么。你知道怎么回事吗

另外,我知道pexpect。请不要向我建议

编辑:经过进一步调查,我已经找到了答案。如果不指定-节点,则私钥将被加密。因此,OpenSSL将立即提示输入PEM密码短语。这意味着stdin.write()的顺序被打乱了。我想另一种选择是使用-节点,然后加密私钥

两个问题:

  • 您没有按照预期的顺序为其提供预期的数据。在某种程度上,它需要一个国家代码,而你却给了它一些其他的数据
  • file对象的
    write()
    方法不会自动插入换行符。您需要在字符串中添加
    “\n”
    ,或者在要输入到程序的每行输入之后将
    “\n”
    字符串分开。例如:
    proc.stdin.write(传递短语+“\n”)
  • 两个问题:

  • 您没有按照预期的顺序为其提供预期的数据。在某种程度上,它需要一个国家代码,而你却给了它一些其他的数据
  • file对象的
    write()
    方法不会自动插入换行符。您需要在字符串中添加
    “\n”
    ,或者在要输入到程序的每行输入之后将
    “\n”
    字符串分开。例如:
    proc.stdin.write(传递短语+“\n”)

  • 代码中存在多个错误,例如,没有向子进程发送换行符

    主要问题是
    openssl
    希望密码短语直接来自终端(如Python中的
    getpass.getpass()
    )。请参阅中的第一个原因:

    首先,应用程序可以绕过标准输出并直接打印到其 控制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
    命令。

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

    主要问题是
    openssl
    希望密码短语直接来自终端(如Python中的
    getpass.getpass()
    )。请参阅中的第一个原因:

    首先,应用程序可以绕过标准输出并直接打印到其 控制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
    命令。

    它看起来像是在寻找一个国家名称,但你没有给它一个。为什么:“我知道。请不要向我建议。”?因为根据我在stackoverflow上的经验,用户倾向于用一般性的回答来回答,希望它能为他们赢得分数,而不是试图真诚地回答问题。它看起来像是在寻找一个国家名称,但你没有给它一个。为什么:“我知道。请不要向我提建议。”?因为从我在stackoverflow的经历来看,用户倾向于用一般性的回答来回答,希望它能为他们赢得分数,而不是试图真诚地回答问题。OpenSSL行为怪异,在允许stdin.write()运行之前,会提示输入PEM密码,从而导致stdin.write()的顺序可能会弄乱。@PythonNoob它可能会直接打开控制PTY。OpenSSL行为怪异,在允许stdin.write()运行之前会提示输入PEM密码短语,从而导致stdin.write()的顺序被弄乱。@PythonNoob它可能会直接打开控制PTY。谢谢您的帮助。我试图避免使用pexpect,因为它不随股票Python提供。如果您不想让用户安装它,您可以将它与您的代码放在一起(不推荐)separately@PythonNoob如果你不想使用pexpect,你必须重新设计它的一部分。谢谢你的帮助。我试图避免使用pexpect,因为它不随股票Python提供。如果您不想让用户安装它,您可以将它与您的代码放在一起(不推荐)separately@PythonNoob如果你不想使用pexpect,你必须重新设计它的一部分。