ec2: add support for creation and importing of real SSH keys (#2108)
* ec2: add support for creation and importing of real SSH keys * setup: lock PyYAML version to avoid incompatibilities
This commit is contained in:
parent
d8ff67197b
commit
fb2a76fd66
@ -58,6 +58,14 @@ class InvalidKeyPairDuplicateError(EC2ClientError):
|
|||||||
.format(key))
|
.format(key))
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidKeyPairFormatError(EC2ClientError):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(InvalidKeyPairFormatError, self).__init__(
|
||||||
|
"InvalidKeyPair.Format",
|
||||||
|
"Key is not in valid OpenSSH public key format")
|
||||||
|
|
||||||
|
|
||||||
class InvalidVPCIdError(EC2ClientError):
|
class InvalidVPCIdError(EC2ClientError):
|
||||||
|
|
||||||
def __init__(self, vpc_id):
|
def __init__(self, vpc_id):
|
||||||
|
@ -20,6 +20,7 @@ from boto.ec2.blockdevicemapping import BlockDeviceMapping, BlockDeviceType
|
|||||||
from boto.ec2.spotinstancerequest import SpotInstanceRequest as BotoSpotRequest
|
from boto.ec2.spotinstancerequest import SpotInstanceRequest as BotoSpotRequest
|
||||||
from boto.ec2.launchspecification import LaunchSpecification
|
from boto.ec2.launchspecification import LaunchSpecification
|
||||||
|
|
||||||
|
|
||||||
from moto.compat import OrderedDict
|
from moto.compat import OrderedDict
|
||||||
from moto.core import BaseBackend
|
from moto.core import BaseBackend
|
||||||
from moto.core.models import Model, BaseModel
|
from moto.core.models import Model, BaseModel
|
||||||
@ -43,6 +44,7 @@ from .exceptions import (
|
|||||||
InvalidInstanceIdError,
|
InvalidInstanceIdError,
|
||||||
InvalidInternetGatewayIdError,
|
InvalidInternetGatewayIdError,
|
||||||
InvalidKeyPairDuplicateError,
|
InvalidKeyPairDuplicateError,
|
||||||
|
InvalidKeyPairFormatError,
|
||||||
InvalidKeyPairNameError,
|
InvalidKeyPairNameError,
|
||||||
InvalidNetworkAclIdError,
|
InvalidNetworkAclIdError,
|
||||||
InvalidNetworkAttachmentIdError,
|
InvalidNetworkAttachmentIdError,
|
||||||
@ -120,6 +122,8 @@ from .utils import (
|
|||||||
random_customer_gateway_id,
|
random_customer_gateway_id,
|
||||||
is_tag_filter,
|
is_tag_filter,
|
||||||
tag_filter_matches,
|
tag_filter_matches,
|
||||||
|
rsa_public_key_parse,
|
||||||
|
rsa_public_key_fingerprint
|
||||||
)
|
)
|
||||||
|
|
||||||
INSTANCE_TYPES = json.load(
|
INSTANCE_TYPES = json.load(
|
||||||
@ -910,7 +914,14 @@ class KeyPairBackend(object):
|
|||||||
def import_key_pair(self, key_name, public_key_material):
|
def import_key_pair(self, key_name, public_key_material):
|
||||||
if key_name in self.keypairs:
|
if key_name in self.keypairs:
|
||||||
raise InvalidKeyPairDuplicateError(key_name)
|
raise InvalidKeyPairDuplicateError(key_name)
|
||||||
keypair = KeyPair(key_name, **random_key_pair())
|
|
||||||
|
try:
|
||||||
|
rsa_public_key = rsa_public_key_parse(public_key_material)
|
||||||
|
except ValueError:
|
||||||
|
raise InvalidKeyPairFormatError()
|
||||||
|
|
||||||
|
fingerprint = rsa_public_key_fingerprint(rsa_public_key)
|
||||||
|
keypair = KeyPair(key_name, material=public_key_material, fingerprint=fingerprint)
|
||||||
self.keypairs[key_name] = keypair
|
self.keypairs[key_name] = keypair
|
||||||
return keypair
|
return keypair
|
||||||
|
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from cryptography.hazmat.primitives import serialization
|
||||||
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
|
import sshpubkeys.exceptions
|
||||||
|
from sshpubkeys.keys import SSHKey
|
||||||
|
|
||||||
|
|
||||||
EC2_RESOURCE_TO_PREFIX = {
|
EC2_RESOURCE_TO_PREFIX = {
|
||||||
'customer-gateway': 'cgw',
|
'customer-gateway': 'cgw',
|
||||||
'dhcp-options': 'dopt',
|
'dhcp-options': 'dopt',
|
||||||
@ -453,23 +462,19 @@ def simple_aws_filter_to_re(filter_string):
|
|||||||
|
|
||||||
|
|
||||||
def random_key_pair():
|
def random_key_pair():
|
||||||
def random_hex():
|
private_key = rsa.generate_private_key(
|
||||||
return chr(random.choice(list(range(48, 58)) + list(range(97, 102))))
|
public_exponent=65537,
|
||||||
|
key_size=2048,
|
||||||
|
backend=default_backend())
|
||||||
|
private_key_material = private_key.private_bytes(
|
||||||
|
encoding=serialization.Encoding.PEM,
|
||||||
|
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||||
|
encryption_algorithm=serialization.NoEncryption())
|
||||||
|
public_key_fingerprint = rsa_public_key_fingerprint(private_key.public_key())
|
||||||
|
|
||||||
def random_fingerprint():
|
|
||||||
return ':'.join([random_hex() + random_hex() for i in range(20)])
|
|
||||||
|
|
||||||
def random_material():
|
|
||||||
return ''.join([
|
|
||||||
chr(random.choice(list(range(65, 91)) + list(range(48, 58)) +
|
|
||||||
list(range(97, 102))))
|
|
||||||
for i in range(1000)
|
|
||||||
])
|
|
||||||
material = "---- BEGIN RSA PRIVATE KEY ----" + random_material() + \
|
|
||||||
"-----END RSA PRIVATE KEY-----"
|
|
||||||
return {
|
return {
|
||||||
'fingerprint': random_fingerprint(),
|
'fingerprint': public_key_fingerprint,
|
||||||
'material': material
|
'material': private_key_material.decode('ascii')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -535,3 +540,28 @@ def generate_instance_identity_document(instance):
|
|||||||
}
|
}
|
||||||
|
|
||||||
return document
|
return document
|
||||||
|
|
||||||
|
|
||||||
|
def rsa_public_key_parse(key_material):
|
||||||
|
try:
|
||||||
|
if not isinstance(key_material, six.binary_type):
|
||||||
|
key_material = key_material.encode("ascii")
|
||||||
|
|
||||||
|
decoded_key = base64.b64decode(key_material).decode("ascii")
|
||||||
|
public_key = SSHKey(decoded_key)
|
||||||
|
except (sshpubkeys.exceptions.InvalidKeyException, UnicodeDecodeError):
|
||||||
|
raise ValueError('bad key')
|
||||||
|
|
||||||
|
if not public_key.rsa:
|
||||||
|
raise ValueError('bad key')
|
||||||
|
|
||||||
|
return public_key.rsa
|
||||||
|
|
||||||
|
|
||||||
|
def rsa_public_key_fingerprint(rsa_public_key):
|
||||||
|
key_data = rsa_public_key.public_bytes(
|
||||||
|
encoding=serialization.Encoding.DER,
|
||||||
|
format=serialization.PublicFormat.SubjectPublicKeyInfo)
|
||||||
|
fingerprint_hex = hashlib.md5(key_data).hexdigest()
|
||||||
|
fingerprint = re.sub(r'([a-f0-9]{2})(?!$)', r'\1:', fingerprint_hex)
|
||||||
|
return fingerprint
|
||||||
|
3
setup.py
3
setup.py
@ -28,7 +28,7 @@ install_requires = [
|
|||||||
"xmltodict",
|
"xmltodict",
|
||||||
"six>1.9",
|
"six>1.9",
|
||||||
"werkzeug",
|
"werkzeug",
|
||||||
"PyYAML",
|
"PyYAML==3.13",
|
||||||
"pytz",
|
"pytz",
|
||||||
"python-dateutil<3.0.0,>=2.1",
|
"python-dateutil<3.0.0,>=2.1",
|
||||||
"python-jose<4.0.0",
|
"python-jose<4.0.0",
|
||||||
@ -39,6 +39,7 @@ install_requires = [
|
|||||||
"responses>=0.9.0",
|
"responses>=0.9.0",
|
||||||
"idna<2.9,>=2.5",
|
"idna<2.9,>=2.5",
|
||||||
"cfn-lint",
|
"cfn-lint",
|
||||||
|
"sshpubkeys>=3.1.0,<4.0"
|
||||||
]
|
]
|
||||||
|
|
||||||
extras_require = {
|
extras_require = {
|
||||||
|
0
tests/test_ec2/__init__.py
Normal file
0
tests/test_ec2/__init__.py
Normal file
15
tests/test_ec2/helpers.py
Normal file
15
tests/test_ec2/helpers.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import six
|
||||||
|
|
||||||
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
from cryptography.hazmat.primitives import serialization
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
|
|
||||||
|
|
||||||
|
def rsa_check_private_key(private_key_material):
|
||||||
|
assert isinstance(private_key_material, six.string_types)
|
||||||
|
|
||||||
|
private_key = serialization.load_pem_private_key(
|
||||||
|
data=private_key_material.encode('ascii'),
|
||||||
|
backend=default_backend(),
|
||||||
|
password=None)
|
||||||
|
assert isinstance(private_key, rsa.RSAPrivateKey)
|
@ -4,12 +4,46 @@ import tests.backport_assert_raises
|
|||||||
from nose.tools import assert_raises
|
from nose.tools import assert_raises
|
||||||
|
|
||||||
import boto
|
import boto
|
||||||
import six
|
|
||||||
import sure # noqa
|
import sure # noqa
|
||||||
|
|
||||||
from boto.exception import EC2ResponseError
|
from boto.exception import EC2ResponseError
|
||||||
from moto import mock_ec2_deprecated
|
from moto import mock_ec2_deprecated
|
||||||
|
|
||||||
|
from .helpers import rsa_check_private_key
|
||||||
|
|
||||||
|
|
||||||
|
RSA_PUBLIC_KEY_OPENSSH = b"""\
|
||||||
|
ssh-rsa \
|
||||||
|
AAAAB3NzaC1yc2EAAAADAQABAAABAQDusXfgTE4eBP50NglSzCSEGnIL6+cr6m3H\
|
||||||
|
6cZANOQ+P1o/W4BdtcAL3sor4iGi7SOeJgo\8kweyMQrhrt6HaKGgromRiz37LQx\
|
||||||
|
4YIAcBi4Zd023mO/V7Rc2Chh18mWgLSmA6ng+j37ip6452zxtv0jHAz9pJolbKBp\
|
||||||
|
JzbZlPN45ZCTk9ck0fSVHRl6VRSSPQcpqi65XpRf+35zNOCGCc1mAOOTmw59Q2a6\
|
||||||
|
A3t8mL7r91aM5q6QOQm219lctFM8O7HRJnDgmhGpnjRwE1LyKktWTbgFZ4SNWU2X\
|
||||||
|
qusUO07jKuSxzPumXBeU+JEtx0J1tqZwJlpGt2R+0qN7nKnPl2+hx \
|
||||||
|
moto@github.com"""
|
||||||
|
|
||||||
|
RSA_PUBLIC_KEY_RFC4716 = b"""\
|
||||||
|
---- BEGIN SSH2 PUBLIC KEY ----
|
||||||
|
AAAAB3NzaC1yc2EAAAADAQABAAABAQDusXfgTE4eBP50NglSzCSEGnIL6+cr6m3H6cZANO
|
||||||
|
Q+P1o/W4BdtcAL3sor4iGi7SOeJgo8kweyMQrhrt6HaKGgromRiz37LQx4YIAcBi4Zd023
|
||||||
|
mO/V7Rc2Chh18mWgLSmA6ng+j37ip6452zxtv0jHAz9pJolbKBpJzbZlPN45ZCTk9ck0fS
|
||||||
|
VHRl6VRSSPQcpqi65XpRf+35zNOCGCc1mAOOTmw59Q2a6A3t8mL7r91aM5q6QOQm219lct
|
||||||
|
FM8O7HRJnDgmhGpnjRwE1LyKktWTbgFZ4SNWU2XqusUO07jKuSxzPumXBeU+JEtx0J1tqZ
|
||||||
|
wJlpGt2R+0qN7nKnPl2+hx
|
||||||
|
---- END SSH2 PUBLIC KEY ----
|
||||||
|
"""
|
||||||
|
|
||||||
|
RSA_PUBLIC_KEY_FINGERPRINT = "6a:49:07:1c:7e:bd:d2:bd:96:25:fe:b5:74:83:ae:fd"
|
||||||
|
|
||||||
|
DSA_PUBLIC_KEY_OPENSSH = b"""ssh-dss \
|
||||||
|
AAAAB3NzaC1kc3MAAACBAJ0aXctVwbN6VB81gpo8R7DUk8zXRjZvrkg8Y8vEGt63gklpNJNsLXtEUXkl5D4c0nD2FZO1rJNqFoe\
|
||||||
|
OQOCoGSfclHvt9w4yPl/lUEtb3Qtj1j80MInETHr19vaSunRk5R+M+8YH+LLcdYdz7MijuGey02mbi0H9K5nUIcuLMArVAAAAFQ\
|
||||||
|
D0RDvsObRWBlnaW8645obZBM86jwAAAIBNZwf3B4krIzAwVfkMHLDSdAvs7lOWE7o8SJLzr9t4a9HhYp9SLbMzJ815KWfidEYV2\
|
||||||
|
+s4ZaPCfcZ1GENFRbE8rixz5eMAjEUXEPMJkblDZTHzMsH96z2cOCQZ0vfOmgznsf18Uf725pqo9OqAioEsTJjX8jtI2qNPEBU0\
|
||||||
|
uhMSZQAAAIBBMGhDu5CWPUlS2QG7vzmzw81XasmHE/s2YPDRbolkriwlunpgwZhCscoQP8HFHY+DLUVvUb+GZwBmFt4l1uHl03b\
|
||||||
|
ffsm7UIHtCBYERr9Nx0u20ldfhkgB1lhaJb5o0ZJ3pmJ38KChfyHe5EUcqRdEFo89Mp72VI2Z6UHyL175RA== \
|
||||||
|
moto@github.com"""
|
||||||
|
|
||||||
|
|
||||||
@mock_ec2_deprecated
|
@mock_ec2_deprecated
|
||||||
def test_key_pairs_empty():
|
def test_key_pairs_empty():
|
||||||
@ -33,14 +67,15 @@ def test_key_pairs_create():
|
|||||||
conn = boto.connect_ec2('the_key', 'the_secret')
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
|
|
||||||
with assert_raises(EC2ResponseError) as ex:
|
with assert_raises(EC2ResponseError) as ex:
|
||||||
kp = conn.create_key_pair('foo', dry_run=True)
|
conn.create_key_pair('foo', dry_run=True)
|
||||||
ex.exception.error_code.should.equal('DryRunOperation')
|
ex.exception.error_code.should.equal('DryRunOperation')
|
||||||
ex.exception.status.should.equal(400)
|
ex.exception.status.should.equal(400)
|
||||||
ex.exception.message.should.equal(
|
ex.exception.message.should.equal(
|
||||||
'An error occurred (DryRunOperation) when calling the CreateKeyPair operation: Request would have succeeded, but DryRun flag is set')
|
'An error occurred (DryRunOperation) when calling the CreateKeyPair operation: Request would have succeeded, but DryRun flag is set')
|
||||||
|
|
||||||
kp = conn.create_key_pair('foo')
|
kp = conn.create_key_pair('foo')
|
||||||
assert kp.material.startswith('---- BEGIN RSA PRIVATE KEY ----')
|
rsa_check_private_key(kp.material)
|
||||||
|
|
||||||
kps = conn.get_all_key_pairs()
|
kps = conn.get_all_key_pairs()
|
||||||
assert len(kps) == 1
|
assert len(kps) == 1
|
||||||
assert kps[0].name == 'foo'
|
assert kps[0].name == 'foo'
|
||||||
@ -49,13 +84,19 @@ def test_key_pairs_create():
|
|||||||
@mock_ec2_deprecated
|
@mock_ec2_deprecated
|
||||||
def test_key_pairs_create_two():
|
def test_key_pairs_create_two():
|
||||||
conn = boto.connect_ec2('the_key', 'the_secret')
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
kp = conn.create_key_pair('foo')
|
|
||||||
kp = conn.create_key_pair('bar')
|
kp1 = conn.create_key_pair('foo')
|
||||||
assert kp.material.startswith('---- BEGIN RSA PRIVATE KEY ----')
|
rsa_check_private_key(kp1.material)
|
||||||
|
|
||||||
|
kp2 = conn.create_key_pair('bar')
|
||||||
|
rsa_check_private_key(kp2.material)
|
||||||
|
|
||||||
|
assert kp1.material != kp2.material
|
||||||
|
|
||||||
kps = conn.get_all_key_pairs()
|
kps = conn.get_all_key_pairs()
|
||||||
kps.should.have.length_of(2)
|
kps.should.have.length_of(2)
|
||||||
[i.name for i in kps].should.contain('foo')
|
assert {i.name for i in kps} == {'foo', 'bar'}
|
||||||
[i.name for i in kps].should.contain('bar')
|
|
||||||
kps = conn.get_all_key_pairs('foo')
|
kps = conn.get_all_key_pairs('foo')
|
||||||
kps.should.have.length_of(1)
|
kps.should.have.length_of(1)
|
||||||
kps[0].name.should.equal('foo')
|
kps[0].name.should.equal('foo')
|
||||||
@ -64,8 +105,7 @@ def test_key_pairs_create_two():
|
|||||||
@mock_ec2_deprecated
|
@mock_ec2_deprecated
|
||||||
def test_key_pairs_create_exist():
|
def test_key_pairs_create_exist():
|
||||||
conn = boto.connect_ec2('the_key', 'the_secret')
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
kp = conn.create_key_pair('foo')
|
conn.create_key_pair('foo')
|
||||||
assert kp.material.startswith('---- BEGIN RSA PRIVATE KEY ----')
|
|
||||||
assert len(conn.get_all_key_pairs()) == 1
|
assert len(conn.get_all_key_pairs()) == 1
|
||||||
|
|
||||||
with assert_raises(EC2ResponseError) as cm:
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
@ -105,23 +145,30 @@ def test_key_pairs_import():
|
|||||||
conn = boto.connect_ec2('the_key', 'the_secret')
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
|
|
||||||
with assert_raises(EC2ResponseError) as ex:
|
with assert_raises(EC2ResponseError) as ex:
|
||||||
kp = conn.import_key_pair('foo', b'content', dry_run=True)
|
conn.import_key_pair('foo', RSA_PUBLIC_KEY_OPENSSH, dry_run=True)
|
||||||
ex.exception.error_code.should.equal('DryRunOperation')
|
ex.exception.error_code.should.equal('DryRunOperation')
|
||||||
ex.exception.status.should.equal(400)
|
ex.exception.status.should.equal(400)
|
||||||
ex.exception.message.should.equal(
|
ex.exception.message.should.equal(
|
||||||
'An error occurred (DryRunOperation) when calling the ImportKeyPair operation: Request would have succeeded, but DryRun flag is set')
|
'An error occurred (DryRunOperation) when calling the ImportKeyPair operation: Request would have succeeded, but DryRun flag is set')
|
||||||
|
|
||||||
kp = conn.import_key_pair('foo', b'content')
|
kp1 = conn.import_key_pair('foo', RSA_PUBLIC_KEY_OPENSSH)
|
||||||
assert kp.name == 'foo'
|
assert kp1.name == 'foo'
|
||||||
|
assert kp1.fingerprint == RSA_PUBLIC_KEY_FINGERPRINT
|
||||||
|
|
||||||
|
kp2 = conn.import_key_pair('foo2', RSA_PUBLIC_KEY_RFC4716)
|
||||||
|
assert kp2.name == 'foo2'
|
||||||
|
assert kp2.fingerprint == RSA_PUBLIC_KEY_FINGERPRINT
|
||||||
|
|
||||||
kps = conn.get_all_key_pairs()
|
kps = conn.get_all_key_pairs()
|
||||||
assert len(kps) == 1
|
assert len(kps) == 2
|
||||||
assert kps[0].name == 'foo'
|
assert kps[0].name == kp1.name
|
||||||
|
assert kps[1].name == kp2.name
|
||||||
|
|
||||||
|
|
||||||
@mock_ec2_deprecated
|
@mock_ec2_deprecated
|
||||||
def test_key_pairs_import_exist():
|
def test_key_pairs_import_exist():
|
||||||
conn = boto.connect_ec2('the_key', 'the_secret')
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
kp = conn.import_key_pair('foo', b'content')
|
kp = conn.import_key_pair('foo', RSA_PUBLIC_KEY_OPENSSH)
|
||||||
assert kp.name == 'foo'
|
assert kp.name == 'foo'
|
||||||
assert len(conn.get_all_key_pairs()) == 1
|
assert len(conn.get_all_key_pairs()) == 1
|
||||||
|
|
||||||
@ -132,6 +179,32 @@ def test_key_pairs_import_exist():
|
|||||||
cm.exception.request_id.should_not.be.none
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2_deprecated
|
||||||
|
def test_key_pairs_invalid():
|
||||||
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
|
|
||||||
|
with assert_raises(EC2ResponseError) as ex:
|
||||||
|
conn.import_key_pair('foo', b'')
|
||||||
|
ex.exception.error_code.should.equal('InvalidKeyPair.Format')
|
||||||
|
ex.exception.status.should.equal(400)
|
||||||
|
ex.exception.message.should.equal(
|
||||||
|
'Key is not in valid OpenSSH public key format')
|
||||||
|
|
||||||
|
with assert_raises(EC2ResponseError) as ex:
|
||||||
|
conn.import_key_pair('foo', b'garbage')
|
||||||
|
ex.exception.error_code.should.equal('InvalidKeyPair.Format')
|
||||||
|
ex.exception.status.should.equal(400)
|
||||||
|
ex.exception.message.should.equal(
|
||||||
|
'Key is not in valid OpenSSH public key format')
|
||||||
|
|
||||||
|
with assert_raises(EC2ResponseError) as ex:
|
||||||
|
conn.import_key_pair('foo', DSA_PUBLIC_KEY_OPENSSH)
|
||||||
|
ex.exception.error_code.should.equal('InvalidKeyPair.Format')
|
||||||
|
ex.exception.status.should.equal(400)
|
||||||
|
ex.exception.message.should.equal(
|
||||||
|
'Key is not in valid OpenSSH public key format')
|
||||||
|
|
||||||
|
|
||||||
@mock_ec2_deprecated
|
@mock_ec2_deprecated
|
||||||
def test_key_pair_filters():
|
def test_key_pair_filters():
|
||||||
conn = boto.connect_ec2('the_key', 'the_secret')
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
from moto.ec2 import utils
|
from moto.ec2 import utils
|
||||||
|
|
||||||
|
from .helpers import rsa_check_private_key
|
||||||
|
|
||||||
|
|
||||||
def test_random_key_pair():
|
def test_random_key_pair():
|
||||||
key_pair = utils.random_key_pair()
|
key_pair = utils.random_key_pair()
|
||||||
assert len(key_pair['fingerprint']) == 59
|
rsa_check_private_key(key_pair['material'])
|
||||||
assert key_pair['material'].startswith('---- BEGIN RSA PRIVATE KEY ----')
|
|
||||||
assert key_pair['material'].endswith('-----END RSA PRIVATE KEY-----')
|
# AWS uses MD5 fingerprints, which are 47 characters long, *not* SHA1
|
||||||
|
# fingerprints with 59 characters.
|
||||||
|
assert len(key_pair['fingerprint']) == 47
|
||||||
|
Loading…
Reference in New Issue
Block a user