You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

185 lines
5.8 KiB

7 years ago
  1. # Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
  2. #
  3. # This file is part of paramiko.
  4. #
  5. # Paramiko is free software; you can redistribute it and/or modify it under the
  6. # terms of the GNU Lesser General Public License as published by the Free
  7. # Software Foundation; either version 2.1 of the License, or (at your option)
  8. # any later version.
  9. #
  10. # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
  11. # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  13. # details.
  14. #
  15. # You should have received a copy of the GNU Lesser General Public License
  16. # along with Paramiko; if not, write to the Free Software Foundation, Inc.,
  17. # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  18. """
  19. RSA keys.
  20. """
  21. from cryptography.exceptions import InvalidSignature
  22. from cryptography.hazmat.backends import default_backend
  23. from cryptography.hazmat.primitives import hashes, serialization
  24. from cryptography.hazmat.primitives.asymmetric import rsa, padding
  25. from paramiko.message import Message
  26. from paramiko.pkey import PKey
  27. from paramiko.py3compat import PY2
  28. from paramiko.ssh_exception import SSHException
  29. class RSAKey(PKey):
  30. """
  31. Representation of an RSA key which can be used to sign and verify SSH2
  32. data.
  33. """
  34. def __init__(self, msg=None, data=None, filename=None, password=None,
  35. key=None, file_obj=None):
  36. self.key = None
  37. self.public_blob = None
  38. if file_obj is not None:
  39. self._from_private_key(file_obj, password)
  40. return
  41. if filename is not None:
  42. self._from_private_key_file(filename, password)
  43. return
  44. if (msg is None) and (data is not None):
  45. msg = Message(data)
  46. if key is not None:
  47. self.key = key
  48. else:
  49. self._check_type_and_load_cert(
  50. msg=msg,
  51. key_type='ssh-rsa',
  52. cert_type='ssh-rsa-cert-v01@openssh.com',
  53. )
  54. self.key = rsa.RSAPublicNumbers(
  55. e=msg.get_mpint(), n=msg.get_mpint()
  56. ).public_key(default_backend())
  57. @property
  58. def size(self):
  59. return self.key.key_size
  60. @property
  61. def public_numbers(self):
  62. if isinstance(self.key, rsa.RSAPrivateKey):
  63. return self.key.private_numbers().public_numbers
  64. else:
  65. return self.key.public_numbers()
  66. def asbytes(self):
  67. m = Message()
  68. m.add_string('ssh-rsa')
  69. m.add_mpint(self.public_numbers.e)
  70. m.add_mpint(self.public_numbers.n)
  71. return m.asbytes()
  72. def __str__(self):
  73. # NOTE: as per inane commentary in #853, this appears to be the least
  74. # crummy way to get a representation that prints identical to Python
  75. # 2's previous behavior, on both interpreters.
  76. # TODO: replace with a nice clean fingerprint display or something
  77. if PY2:
  78. # Can't just return the .decode below for Py2 because stuff still
  79. # tries stuffing it into ASCII for whatever godforsaken reason
  80. return self.asbytes()
  81. else:
  82. return self.asbytes().decode('utf8', errors='ignore')
  83. def __hash__(self):
  84. return hash((self.get_name(), self.public_numbers.e,
  85. self.public_numbers.n))
  86. def get_name(self):
  87. return 'ssh-rsa'
  88. def get_bits(self):
  89. return self.size
  90. def can_sign(self):
  91. return isinstance(self.key, rsa.RSAPrivateKey)
  92. def sign_ssh_data(self, data):
  93. sig = self.key.sign(
  94. data,
  95. padding=padding.PKCS1v15(),
  96. algorithm=hashes.SHA1(),
  97. )
  98. m = Message()
  99. m.add_string('ssh-rsa')
  100. m.add_string(sig)
  101. return m
  102. def verify_ssh_sig(self, data, msg):
  103. if msg.get_text() != 'ssh-rsa':
  104. return False
  105. key = self.key
  106. if isinstance(key, rsa.RSAPrivateKey):
  107. key = key.public_key()
  108. try:
  109. key.verify(
  110. msg.get_binary(), data, padding.PKCS1v15(), hashes.SHA1()
  111. )
  112. except InvalidSignature:
  113. return False
  114. else:
  115. return True
  116. def write_private_key_file(self, filename, password=None):
  117. self._write_private_key_file(
  118. filename,
  119. self.key,
  120. serialization.PrivateFormat.TraditionalOpenSSL,
  121. password=password
  122. )
  123. def write_private_key(self, file_obj, password=None):
  124. self._write_private_key(
  125. file_obj,
  126. self.key,
  127. serialization.PrivateFormat.TraditionalOpenSSL,
  128. password=password
  129. )
  130. @staticmethod
  131. def generate(bits, progress_func=None):
  132. """
  133. Generate a new private RSA key. This factory function can be used to
  134. generate a new host key or authentication key.
  135. :param int bits: number of bits the generated key should be.
  136. :param progress_func: Unused
  137. :return: new `.RSAKey` private key
  138. """
  139. key = rsa.generate_private_key(
  140. public_exponent=65537, key_size=bits, backend=default_backend()
  141. )
  142. return RSAKey(key=key)
  143. # ...internals...
  144. def _from_private_key_file(self, filename, password):
  145. data = self._read_private_key_file('RSA', filename, password)
  146. self._decode_key(data)
  147. def _from_private_key(self, file_obj, password):
  148. data = self._read_private_key('RSA', file_obj, password)
  149. self._decode_key(data)
  150. def _decode_key(self, data):
  151. try:
  152. key = serialization.load_der_private_key(
  153. data, password=None, backend=default_backend()
  154. )
  155. except ValueError as e:
  156. raise SSHException(str(e))
  157. assert isinstance(key, rsa.RSAPrivateKey)
  158. self.key = key

Powered by TurnKey Linux.