【问题标题】:How to generate SSH key pairs with Python如何使用 Python 生成 SSH 密钥对
【发布时间】:2011-01-28 19:30:53
【问题描述】:

我正在尝试编写一个脚本来为我生成 SSH 身份密钥对。

from M2Crypto import RSA
key = RSA.gen_key(1024, 65337)
key.save_key("/tmp/my.key", cipher=None)

文件/tmp/my.key 现在看起来很棒。

通过运行ssh-keygen -y -f /tmp/my.key > /tmp/my.key.pub,我可以提取公钥。

我的问题是如何从 python 中提取公钥?使用key.save_pub_key("/tmp/my.key.pub") 可以节省如下内容:

-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADASDASDASDASDBarYRsmMazM1hd7a+u3QeMP
...
FZQ7Ic+BmmeWHvvVP4Yjyu1t6vAut7mKkaDeKbT3yiGVUgAEUaWMXqECAwEAAQ==
-----END PUBLIC KEY-----

当我在寻找类似的东西时:

ssh-rsa AAAABCASDDBM$%3WEAv/3%$F ..... OSDFKJSL43$%^DFg==

【问题讨论】:

标签: python ssh m2crypto ssh-keys


【解决方案1】:

使用cryptographypycrypto 不再处于积极开发中,如果可能,您应该使用密码学。自 6 月起,也可以生成 SSH 公钥:

from cryptography.hazmat.primitives import serialization as crypto_serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend as crypto_default_backend

key = rsa.generate_private_key(
    backend=crypto_default_backend(),
    public_exponent=65537,
    key_size=2048
)

private_key = key.private_bytes(
    crypto_serialization.Encoding.PEM,
    crypto_serialization.PrivateFormat.PKCS8,
    crypto_serialization.NoEncryption()
)

public_key = key.public_key().public_bytes(
    crypto_serialization.Encoding.OpenSSH,
    crypto_serialization.PublicFormat.OpenSSH
)

注意:您至少需要版本1.4.0

注意:如果您的 SSH 客户端不理解这种私钥格式,replace PKCS8TraditionalOpenSSL

【讨论】:

  • 注意点:cryptography >= 1.4.0 的版本要求和缺少导入语句:from cryptography.hazmat.backends import default_backend as crypto_default_backend
  • 谢谢!相应调整。
  • 如何将其转换为其他格式,即 `ssh-rsa AAAABCASDDBM$%3WEAv/3%$F ..... OSDFKJSL43$%^DFg=='
【解决方案2】:

以防万一有任何未来的旅行者希望这样做。 RSA 模块现在支持以 OpenSSH 格式写出公钥(在之前的帖子中可能没有)。所以我认为你可以做你需要的:

from os import chmod
from Crypto.PublicKey import RSA

key = RSA.generate(2048)
with open("/tmp/private.key", 'wb') as content_file:
    chmod("/tmp/private.key", 0600)
    content_file.write(key.exportKey('PEM'))
pubkey = key.publickey()
with open("/tmp/public.key", 'wb') as content_file:
    content_file.write(pubkey.exportKey('OpenSSH'))

文件以“wb”打开,因为密钥必须以二进制模式写入。 显然不要将您的私钥存储在 /tmp...

【讨论】:

  • key.exportKey('PEM')中,参数表示输出格式。共有三个选项:'DER' - 二进制编码,'PEM' - 纹理编码,'OpenSSH' - 根据 OpenSSH 规范进行纹理编码。
  • @signal 根据the documentation,用于导出的OpenSSH“只适用于公钥(而非私钥)”。
  • 我相信您引用的短语与使用带有 exportKey 的“OpenSSH”格式选项而不是 exportKey 方法本身有关,例如文档说您可以使用“OpenSSH”格式 arg 作为公钥,因为我已经完成,并且像我所做的那样为私钥“PEM”。
  • 为此使用密码学而不是 pycrypto;查看其他答案stackoverflow.com/a/39126754#1301627
  • 在 Python3 中,八进制文字必须有一个“0o”前缀,所以:chmod("/tmp/private.key", 0600) 变为 chmod("/tmp/private.key", 0o600)
【解决方案3】:

编辑 05/09/2012:

我刚刚意识到 pycrypto 已经有了这个:

import os
from Crypto.PublicKey import RSA

key = RSA.generate(2048, os.urandom)
print key.exportKey('OpenSSH')

此代码适用于我:

import os
from Crypto.PublicKey import RSA

key = RSA.generate(2048, os.urandom)

# Create public key.                                                                                                                                               
ssh_rsa = '00000007' + base64.b16encode('ssh-rsa')

# Exponent.                                                                                                                                                        
exponent = '%x' % (key.e, )
if len(exponent) % 2:
    exponent = '0' + exponent

ssh_rsa += '%08x' % (len(exponent) / 2, )
ssh_rsa += exponent

modulus = '%x' % (key.n, )
if len(modulus) % 2:
    modulus = '0' + modulus

if modulus[0] in '89abcdef':
    modulus = '00' + modulus

ssh_rsa += '%08x' % (len(modulus) / 2, )
ssh_rsa += modulus

public_key = 'ssh-rsa %s' % (
    base64.b64encode(base64.b16decode(ssh_rsa.upper())), )

【讨论】:

【解决方案4】:

ssh 使用的密钥只是 base64 编码的,我不太了解 M2Crypto,但经过快速浏览后,您似乎可以这样做:

import os
from base64 import b64encode
from M2Crypto import RSA            

key = RSA.gen_key(1024, 65537)
raw_key = key.pub()[1]
b64key = b64encode(raw_key)

username = os.getlogin()
hostname = os.uname()[1]
keystring = 'ssh-rsa %s %s@%s' % (b64key, username, hostname)

with open(os.getenv('HOME')+'/.ssh/id_rsa.pub') as keyfile:
    keyfile.write(keystring)

我没有使用 SSH 测试生成的密钥,所以请告诉我它是否有效(我应该这样认为)

【讨论】:

  • 这太接近我要找的了。不幸的是,我不确定它是否有效。 b64 编码的字符几乎与 ssh-keygen 输出的匹配,但在第一个 AAAA 和其余密钥之间还有 24 个字符。即,b64 密钥看起来像“ssh-rsa AAAAabcdef...==”,而 ssh-keygen 密钥看起来像“ssh-rsa AAAAabcdef...==”还有其他提示吗?
【解决方案5】:

ssh-keygen的base64解码版本输出到key.pub()的内容,keyfile的格式是

b64encode('\x00\x00\x00\x07ssh-rsa%s%s' % (key.pub()[0], key.pub()[1]))

【讨论】:

  • 再看前4个字节代表字符串ssh-rsa的长度,后面是key.pub()[0]中的字节,所以很容易构造。
【解决方案6】:

如果你愿意,你也可以使用ssh-keygen 本身。 你可以扩展它来创建你的文件,然后使用open 稍后阅读内容,但我专注于从这里已经存在的密钥创建一个 .pub 密钥。

from subprocess import Popen, PIPE
import os

home = f'{os.path.expanduser("~")}'
cert_pos = f'{home}/.ssh/my_key'
your_key_pw = ''

cmd = ['ssh-keygen', '-y', '-f', cert_pos]
if your_key_pw:
    cmd.append('-P')
    cmd.append(your_key_pw)

p = Popen(cmd, stdout=PIPE)
p.wait()
res, err = p.communicate()

cert_content = res.decode('utf-8')

【讨论】:

    【解决方案7】:

    这是一个使用 Twisted Conch 库的示例,该库在后台利用 PyCrypto。您可以在http://twistedmatrix.com/documents/current/api/twisted.conch.ssh.keys.html 找到 API 文档:

    from twisted.conch.ssh import keys
    
    # one-time use key
    k="""-----BEGIN RSA PRIVATE KEY-----
    PRIVATE KEY STUFF
    -----END RSA PRIVATE KEY-----"""
    
    # create pycrypto RSA object
    rsa = keys.RSA.importKey(k)
    
    # create `twisted.conch.ssh.keys.Key` instance which has some nice helpers
    key = keys.Key(rsa)
    
    # pull the public part of the key and export an openssh version
    ssh_public = key.public().toString("openssh")
    print ssh_public
    

    【讨论】:

      【解决方案8】:

      您可以按照documentation 中的说明使用pycryptodome

      from Crypto.PublicKey import RSA
      
      key = RSA.generate(2048)
      private_key = key.export_key()
      file_out = open("private.pem", "wb")
      file_out.write(private_key)
      
      public_key = key.publickey().export_key()
      file_out = open("receiver.pem", "wb")
      file_out.write(public_key)
      

      【讨论】:

        【解决方案9】:

        只是猜测......但你有没有尝试过这样的事情?:

        print "ssh-rsa " + "".join([ l.strip() for l in open('/tmp/my.key.pub') if not l.startswith('-----')])
        

        【讨论】:

          【解决方案10】:

          当它是一个对象时,你能从中取出 AAAA...Dfg== 字符串吗?如果是这样,您可以简单地自己打开一个文件并保存它,而不是使用内置的 save_pub_key 函数。

          【讨论】:

            【解决方案11】:

            我不知道有这样的 Python 标准库。

            如果您想查看第三方库,您可能会发现 paramiko 库很有用(还有 available from PyPI)。它实现了 SSH 协议,并具有处理现有密钥但不生成它们的功能。

            生成密钥可能是对该库的有用补充(您可以 work with the developers 将其合并到 Paramiko 库中),并且比从头开始更容易。

            【讨论】:

              猜你喜欢
              • 2014-08-19
              • 1970-01-01
              • 2021-09-10
              • 2015-02-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-01-14
              • 2013-03-17
              相关资源
              最近更新 更多