rsa_backend.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. import binascii
  2. import warnings
  3. import rsa as pyrsa
  4. import rsa.pem as pyrsa_pem
  5. from pyasn1.error import PyAsn1Error
  6. from rsa import DecryptionError
  7. from jose.backends._asn1 import (
  8. rsa_private_key_pkcs1_to_pkcs8,
  9. rsa_private_key_pkcs8_to_pkcs1,
  10. rsa_public_key_pkcs1_to_pkcs8,
  11. )
  12. from jose.backends.base import Key
  13. from jose.constants import ALGORITHMS
  14. from jose.exceptions import JWEError, JWKError
  15. from jose.utils import base64_to_long, long_to_base64
  16. ALGORITHMS.SUPPORTED.remove(ALGORITHMS.RSA_OAEP) # RSA OAEP not supported
  17. LEGACY_INVALID_PKCS8_RSA_HEADER = binascii.unhexlify(
  18. "30" # sequence
  19. "8204BD" # DER-encoded sequence contents length of 1213 bytes -- INCORRECT STATIC LENGTH
  20. "020100" # integer: 0 -- Version
  21. "30" # sequence
  22. "0D" # DER-encoded sequence contents length of 13 bytes -- PrivateKeyAlgorithmIdentifier
  23. "06092A864886F70D010101" # OID -- rsaEncryption
  24. "0500" # NULL -- parameters
  25. )
  26. ASN1_SEQUENCE_ID = binascii.unhexlify("30")
  27. RSA_ENCRYPTION_ASN1_OID = "1.2.840.113549.1.1.1"
  28. # Functions gcd and rsa_recover_prime_factors were copied from cryptography 1.9
  29. # to enable pure python rsa module to be in compliance with section 6.3.1 of RFC7518
  30. # which requires only private exponent (d) for private key.
  31. def _gcd(a, b):
  32. """Calculate the Greatest Common Divisor of a and b.
  33. Unless b==0, the result will have the same sign as b (so that when
  34. b is divided by it, the result comes out positive).
  35. """
  36. while b:
  37. a, b = b, (a % b)
  38. return a
  39. # Controls the number of iterations rsa_recover_prime_factors will perform
  40. # to obtain the prime factors. Each iteration increments by 2 so the actual
  41. # maximum attempts is half this number.
  42. _MAX_RECOVERY_ATTEMPTS = 1000
  43. def _rsa_recover_prime_factors(n, e, d):
  44. """
  45. Compute factors p and q from the private exponent d. We assume that n has
  46. no more than two factors. This function is adapted from code in PyCrypto.
  47. """
  48. # See 8.2.2(i) in Handbook of Applied Cryptography.
  49. ktot = d * e - 1
  50. # The quantity d*e-1 is a multiple of phi(n), even,
  51. # and can be represented as t*2^s.
  52. t = ktot
  53. while t % 2 == 0:
  54. t = t // 2
  55. # Cycle through all multiplicative inverses in Zn.
  56. # The algorithm is non-deterministic, but there is a 50% chance
  57. # any candidate a leads to successful factoring.
  58. # See "Digitalized Signatures and Public Key Functions as Intractable
  59. # as Factorization", M. Rabin, 1979
  60. spotted = False
  61. a = 2
  62. while not spotted and a < _MAX_RECOVERY_ATTEMPTS:
  63. k = t
  64. # Cycle through all values a^{t*2^i}=a^k
  65. while k < ktot:
  66. cand = pow(a, k, n)
  67. # Check if a^k is a non-trivial root of unity (mod n)
  68. if cand != 1 and cand != (n - 1) and pow(cand, 2, n) == 1:
  69. # We have found a number such that (cand-1)(cand+1)=0 (mod n).
  70. # Either of the terms divides n.
  71. p = _gcd(cand + 1, n)
  72. spotted = True
  73. break
  74. k *= 2
  75. # This value was not any good... let's try another!
  76. a += 2
  77. if not spotted:
  78. raise ValueError("Unable to compute factors p and q from exponent d.")
  79. # Found !
  80. q, r = divmod(n, p)
  81. assert r == 0
  82. p, q = sorted((p, q), reverse=True)
  83. return (p, q)
  84. def pem_to_spki(pem, fmt="PKCS8"):
  85. key = RSAKey(pem, ALGORITHMS.RS256)
  86. return key.to_pem(fmt)
  87. def _legacy_private_key_pkcs8_to_pkcs1(pkcs8_key):
  88. """Legacy RSA private key PKCS8-to-PKCS1 conversion.
  89. .. warning::
  90. This is incorrect parsing and only works because the legacy PKCS1-to-PKCS8
  91. encoding was also incorrect.
  92. """
  93. # Only allow this processing if the prefix matches
  94. # AND the following byte indicates an ASN1 sequence,
  95. # as we would expect with the legacy encoding.
  96. if not pkcs8_key.startswith(LEGACY_INVALID_PKCS8_RSA_HEADER + ASN1_SEQUENCE_ID):
  97. raise ValueError("Invalid private key encoding")
  98. return pkcs8_key[len(LEGACY_INVALID_PKCS8_RSA_HEADER) :]
  99. class RSAKey(Key):
  100. SHA256 = "SHA-256"
  101. SHA384 = "SHA-384"
  102. SHA512 = "SHA-512"
  103. def __init__(self, key, algorithm):
  104. if algorithm not in ALGORITHMS.RSA:
  105. raise JWKError("hash_alg: %s is not a valid hash algorithm" % algorithm)
  106. if algorithm in ALGORITHMS.RSA_KW and algorithm != ALGORITHMS.RSA1_5:
  107. raise JWKError("alg: %s is not supported by the RSA backend" % algorithm)
  108. self.hash_alg = {
  109. ALGORITHMS.RS256: self.SHA256,
  110. ALGORITHMS.RS384: self.SHA384,
  111. ALGORITHMS.RS512: self.SHA512,
  112. }.get(algorithm)
  113. self._algorithm = algorithm
  114. if isinstance(key, dict):
  115. self._prepared_key = self._process_jwk(key)
  116. return
  117. if isinstance(key, (pyrsa.PublicKey, pyrsa.PrivateKey)):
  118. self._prepared_key = key
  119. return
  120. if isinstance(key, str):
  121. key = key.encode("utf-8")
  122. if isinstance(key, bytes):
  123. try:
  124. self._prepared_key = pyrsa.PublicKey.load_pkcs1(key)
  125. except ValueError:
  126. try:
  127. self._prepared_key = pyrsa.PublicKey.load_pkcs1_openssl_pem(key)
  128. except ValueError:
  129. try:
  130. self._prepared_key = pyrsa.PrivateKey.load_pkcs1(key)
  131. except ValueError:
  132. try:
  133. der = pyrsa_pem.load_pem(key, b"PRIVATE KEY")
  134. try:
  135. pkcs1_key = rsa_private_key_pkcs8_to_pkcs1(der)
  136. except PyAsn1Error:
  137. # If the key was encoded using the old, invalid,
  138. # encoding then pyasn1 will throw an error attempting
  139. # to parse the key.
  140. pkcs1_key = _legacy_private_key_pkcs8_to_pkcs1(der)
  141. self._prepared_key = pyrsa.PrivateKey.load_pkcs1(pkcs1_key, format="DER")
  142. except ValueError as e:
  143. raise JWKError(e)
  144. return
  145. raise JWKError("Unable to parse an RSA_JWK from key: %s" % key)
  146. def _process_jwk(self, jwk_dict):
  147. if not jwk_dict.get("kty") == "RSA":
  148. raise JWKError("Incorrect key type. Expected: 'RSA', Received: %s" % jwk_dict.get("kty"))
  149. e = base64_to_long(jwk_dict.get("e"))
  150. n = base64_to_long(jwk_dict.get("n"))
  151. if "d" not in jwk_dict:
  152. return pyrsa.PublicKey(e=e, n=n)
  153. else:
  154. d = base64_to_long(jwk_dict.get("d"))
  155. extra_params = ["p", "q", "dp", "dq", "qi"]
  156. if any(k in jwk_dict for k in extra_params):
  157. # Precomputed private key parameters are available.
  158. if not all(k in jwk_dict for k in extra_params):
  159. # These values must be present when 'p' is according to
  160. # Section 6.3.2 of RFC7518, so if they are not we raise
  161. # an error.
  162. raise JWKError("Precomputed private key parameters are incomplete.")
  163. p = base64_to_long(jwk_dict["p"])
  164. q = base64_to_long(jwk_dict["q"])
  165. return pyrsa.PrivateKey(e=e, n=n, d=d, p=p, q=q)
  166. else:
  167. p, q = _rsa_recover_prime_factors(n, e, d)
  168. return pyrsa.PrivateKey(n=n, e=e, d=d, p=p, q=q)
  169. def sign(self, msg):
  170. return pyrsa.sign(msg, self._prepared_key, self.hash_alg)
  171. def verify(self, msg, sig):
  172. if not self.is_public():
  173. warnings.warn("Attempting to verify a message with a private key. " "This is not recommended.")
  174. try:
  175. pyrsa.verify(msg, sig, self._prepared_key)
  176. return True
  177. except pyrsa.pkcs1.VerificationError:
  178. return False
  179. def is_public(self):
  180. return isinstance(self._prepared_key, pyrsa.PublicKey)
  181. def public_key(self):
  182. if isinstance(self._prepared_key, pyrsa.PublicKey):
  183. return self
  184. return self.__class__(pyrsa.PublicKey(n=self._prepared_key.n, e=self._prepared_key.e), self._algorithm)
  185. def to_pem(self, pem_format="PKCS8"):
  186. if isinstance(self._prepared_key, pyrsa.PrivateKey):
  187. der = self._prepared_key.save_pkcs1(format="DER")
  188. if pem_format == "PKCS8":
  189. pkcs8_der = rsa_private_key_pkcs1_to_pkcs8(der)
  190. pem = pyrsa_pem.save_pem(pkcs8_der, pem_marker="PRIVATE KEY")
  191. elif pem_format == "PKCS1":
  192. pem = pyrsa_pem.save_pem(der, pem_marker="RSA PRIVATE KEY")
  193. else:
  194. raise ValueError(f"Invalid pem format specified: {pem_format!r}")
  195. else:
  196. if pem_format == "PKCS8":
  197. pkcs1_der = self._prepared_key.save_pkcs1(format="DER")
  198. pkcs8_der = rsa_public_key_pkcs1_to_pkcs8(pkcs1_der)
  199. pem = pyrsa_pem.save_pem(pkcs8_der, pem_marker="PUBLIC KEY")
  200. elif pem_format == "PKCS1":
  201. der = self._prepared_key.save_pkcs1(format="DER")
  202. pem = pyrsa_pem.save_pem(der, pem_marker="RSA PUBLIC KEY")
  203. else:
  204. raise ValueError(f"Invalid pem format specified: {pem_format!r}")
  205. return pem
  206. def to_dict(self):
  207. if not self.is_public():
  208. public_key = self.public_key()._prepared_key
  209. else:
  210. public_key = self._prepared_key
  211. data = {
  212. "alg": self._algorithm,
  213. "kty": "RSA",
  214. "n": long_to_base64(public_key.n).decode("ASCII"),
  215. "e": long_to_base64(public_key.e).decode("ASCII"),
  216. }
  217. if not self.is_public():
  218. data.update(
  219. {
  220. "d": long_to_base64(self._prepared_key.d).decode("ASCII"),
  221. "p": long_to_base64(self._prepared_key.p).decode("ASCII"),
  222. "q": long_to_base64(self._prepared_key.q).decode("ASCII"),
  223. "dp": long_to_base64(self._prepared_key.exp1).decode("ASCII"),
  224. "dq": long_to_base64(self._prepared_key.exp2).decode("ASCII"),
  225. "qi": long_to_base64(self._prepared_key.coef).decode("ASCII"),
  226. }
  227. )
  228. return data
  229. def wrap_key(self, key_data):
  230. if not self.is_public():
  231. warnings.warn("Attempting to encrypt a message with a private key." " This is not recommended.")
  232. wrapped_key = pyrsa.encrypt(key_data, self._prepared_key)
  233. return wrapped_key
  234. def unwrap_key(self, wrapped_key):
  235. try:
  236. unwrapped_key = pyrsa.decrypt(wrapped_key, self._prepared_key)
  237. except DecryptionError as e:
  238. raise JWEError(e)
  239. return unwrapped_key