replace ssh-keygen -m with a python equivalent
When running on latest released versions of Debian and RHEL/CentOS we get Encryption failure with "ssh-keygen: illegal option -- m" Fixes LP# 1102501 Change-Id: Ia54bf8f3e8d51c8baa09ba67d2e18ad214316989 NOTE: new dependency on pyasn1 python module
This commit is contained in:
parent
139d15a405
commit
8d3933d3a1
@ -24,9 +24,15 @@ Includes root and intermediate CAs, SSH key_pairs and x509 certificates.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import os
|
||||
import re
|
||||
import string
|
||||
import struct
|
||||
|
||||
from pyasn1.codec.der import encoder as der_encoder
|
||||
from pyasn1.type import univ
|
||||
|
||||
from nova import context
|
||||
from nova import db
|
||||
@ -181,23 +187,75 @@ def decrypt_text(project_id, text):
|
||||
raise exception.DecryptionFailure(reason=exc.stderr)
|
||||
|
||||
|
||||
_RSA_OID = univ.ObjectIdentifier('1.2.840.113549.1.1.1')
|
||||
|
||||
|
||||
def _to_sequence(*vals):
|
||||
seq = univ.Sequence()
|
||||
for i in range(len(vals)):
|
||||
seq.setComponentByPosition(i, vals[i])
|
||||
return seq
|
||||
|
||||
|
||||
def convert_from_sshrsa_to_pkcs8(pubkey):
|
||||
"""Convert a ssh public key to openssl format
|
||||
Equivalent to the ssh-keygen's -m option
|
||||
"""
|
||||
# get the second field from the public key file.
|
||||
try:
|
||||
keydata = base64.b64decode(pubkey.split(None)[1])
|
||||
except IndexError:
|
||||
msg = _("Unable to find the key")
|
||||
raise exception.EncryptionFailure(reason=msg)
|
||||
|
||||
# decode the parts of the key
|
||||
parts = []
|
||||
while keydata:
|
||||
dlen = struct.unpack('>I', keydata[:4])[0]
|
||||
data = keydata[4:dlen + 4]
|
||||
keydata = keydata[4 + dlen:]
|
||||
parts.append(data)
|
||||
|
||||
# Use asn to build the openssl key structure
|
||||
#
|
||||
# SEQUENCE(2 elem)
|
||||
# +- SEQUENCE(2 elem)
|
||||
# | +- OBJECT IDENTIFIER (1.2.840.113549.1.1.1)
|
||||
# | +- NULL
|
||||
# +- BIT STRING(1 elem)
|
||||
# +- SEQUENCE(2 elem)
|
||||
# +- INTEGER(2048 bit)
|
||||
# +- INTEGER 65537
|
||||
|
||||
# Build the sequence for the bit string
|
||||
n_val = eval(
|
||||
'0x' + ''.join(['%02X' % struct.unpack('B', x)[0] for x in parts[2]]))
|
||||
e_val = eval(
|
||||
'0x' + ''.join(['%02X' % struct.unpack('B', x)[0] for x in parts[1]]))
|
||||
pkinfo = _to_sequence(univ.Integer(n_val), univ.Integer(e_val))
|
||||
|
||||
# Convert the sequence into a bit string
|
||||
pklong = long(der_encoder.encode(pkinfo).encode('hex'), 16)
|
||||
pkbitstring = univ.BitString("'00%s'B" % bin(pklong)[2:])
|
||||
|
||||
# Build the key data structure
|
||||
oid = _to_sequence(_RSA_OID, univ.Null())
|
||||
pkcs1_seq = _to_sequence(oid, pkbitstring)
|
||||
pkcs8 = base64.encodestring(der_encoder.encode(pkcs1_seq))
|
||||
|
||||
# Remove the embedded new line and format the key, each line
|
||||
# should be 64 characters long
|
||||
return ('-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n' %
|
||||
re.sub("(.{64})", "\\1\n", pkcs8.replace('\n', ''), re.DOTALL))
|
||||
|
||||
|
||||
def ssh_encrypt_text(ssh_public_key, text):
|
||||
"""Encrypt text with an ssh public key.
|
||||
|
||||
Requires recent ssh-keygen binary in addition to openssl binary.
|
||||
"""
|
||||
with utils.tempdir() as tmpdir:
|
||||
sshkey = os.path.abspath(os.path.join(tmpdir, 'ssh.key'))
|
||||
with open(sshkey, 'w') as f:
|
||||
f.write(ssh_public_key)
|
||||
sslkey = os.path.abspath(os.path.join(tmpdir, 'ssl.key'))
|
||||
try:
|
||||
# NOTE(vish): -P is to skip prompt on bad keys
|
||||
out, _err = utils.execute('ssh-keygen',
|
||||
'-P', '',
|
||||
'-e',
|
||||
'-f', sshkey,
|
||||
'-m', 'PKCS8')
|
||||
out = convert_from_sshrsa_to_pkcs8(ssh_public_key)
|
||||
with open(sslkey, 'w') as f:
|
||||
f.write(out)
|
||||
enc, _err = utils.execute('openssl',
|
||||
|
@ -212,3 +212,31 @@ e6fCXWECgYEAqgpGvva5kJ1ISgNwnJbwiNw0sOT9BMOsdNZBElf0kJIIy6FMPvap
|
||||
def test_ssh_encrypt_failure(self):
|
||||
self.assertRaises(exception.EncryptionFailure,
|
||||
crypto.ssh_encrypt_text, '', self.text)
|
||||
|
||||
|
||||
class ConversionTests(test.TestCase):
|
||||
k1 = ("ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA4CqmrxfU7x4sJrubpMNxeglul+d"
|
||||
"ByrsicnvQcHDEjPzdvoz+BaoAG9bjCA5mCeTBIISsVTVXz/hxNeiuBV6LH/UR/c"
|
||||
"27yl53ypN+821ImoexQZcKItdnjJ3gVZlDob1f9+1qDVy63NJ1c+TstkrCTRVeo"
|
||||
"9VyE7RpdSS4UCiBe8Xwk3RkedioFxePrI0Ktc2uASw2G0G2Rl7RN7KZOJbCivfF"
|
||||
"LQMAOu6e+7fYvuE1gxGHHj7dxaBY/ioGOm1W4JmQ1V7AKt19zTBlZKduN8FQMSF"
|
||||
"r35CDlvoWs0+OP8nwlebKNCi/5sdL8qiSLrAcPB4LqdkAf/blNSVA2Yl83/c4lQ"
|
||||
"== test@test")
|
||||
|
||||
k2 = ("-----BEGIN PUBLIC KEY-----\n"
|
||||
"MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEA4CqmrxfU7x4sJrubpMNx\n"
|
||||
"eglul+dByrsicnvQcHDEjPzdvoz+BaoAG9bjCA5mCeTBIISsVTVXz/hxNeiuBV6L\n"
|
||||
"H/UR/c27yl53ypN+821ImoexQZcKItdnjJ3gVZlDob1f9+1qDVy63NJ1c+TstkrC\n"
|
||||
"TRVeo9VyE7RpdSS4UCiBe8Xwk3RkedioFxePrI0Ktc2uASw2G0G2Rl7RN7KZOJbC\n"
|
||||
"ivfFLQMAOu6e+7fYvuE1gxGHHj7dxaBY/ioGOm1W4JmQ1V7AKt19zTBlZKduN8FQ\n"
|
||||
"MSFr35CDlvoWs0+OP8nwlebKNCi/5sdL8qiSLrAcPB4LqdkAf/blNSVA2Yl83/c4\n"
|
||||
"lQIBIw==\n"
|
||||
"-----END PUBLIC KEY-----\n")
|
||||
|
||||
def test_convert_keys(self):
|
||||
result = crypto.convert_from_sshrsa_to_pkcs8(self.k1)
|
||||
self.assertEqual(result, self.k2)
|
||||
|
||||
def test_convert_failure(self):
|
||||
self.assertRaises(exception.EncryptionFailure,
|
||||
crypto.convert_from_sshrsa_to_pkcs8, '')
|
||||
|
@ -16,6 +16,7 @@ sqlalchemy-migrate>=0.7.2
|
||||
netaddr
|
||||
suds==0.4
|
||||
paramiko
|
||||
pyasn1
|
||||
Babel>=0.9.6
|
||||
iso8601>=0.1.4
|
||||
httplib2
|
||||
|
Loading…
x
Reference in New Issue
Block a user