Skip to content

Instantly share code, notes, and snippets.

@pilotmoon
Forked from flavienbwk/rsatool.py
Last active May 8, 2024 13:36
Show Gist options
  • Save pilotmoon/36378853bb2713638ad9f496101b9815 to your computer and use it in GitHub Desktop.
Save pilotmoon/36378853bb2713638ad9f496101b9815 to your computer and use it in GitHub Desktop.
Generates all OpenSSL private key componments from two of the raw primitives: modulus (n), primes p and q, private exponent (d)
#!/usr/bin/env python3
# taken from https://gist.github.com/flavienbwk/54671449419e1576c2708c9a3a711d78
# and modified to work with python3. i also stripped out the pem generation and just dump all the parameters.
# Usage : python rsatool.py -n <decimal_modulus> -p <decimal_prime1> -q <decimal_prime2> -e <decimal_public_exponent>
# or : python rsatool.py -n <decimal_modulus> -d <decimal_private_exponent> -e <decimal_public_exponent>
import base64, fractions, optparse, random, math, gmpy2 as gmpy
def factor_modulus(n, d, e):
"""
Efficiently recover non-trivial factors of n
See: Handbook of Applied Cryptography
8.2.2 Security of RSA -> (i) Relation to factoring (p.287)
http://www.cacr.math.uwaterloo.ca/hac/
"""
t = (e * d - 1)
s = 0
while True:
quotient, remainder = divmod(t, 2)
if remainder != 0:
break
s += 1
t = quotient
found = False
while not found:
i = 1
a = random.randint(1,n-1)
while i <= s and not found:
c1 = pow(a, pow(2, i-1, n) * t, n)
c2 = pow(a, pow(2, i, n) * t, n)
found = c1 != 1 and c1 != (-1 % n) and c2 == 1
i += 1
p = math.gcd(c1-1, n)
q = n // p
return p, q
class RSA:
def __init__(self, p=None, q=None, n=None, d=None, e=None):
"""
Initialize RSA instance using primes (p, q)
or modulus and private exponent (n, d)
"""
if e is None:
raise ValueError('Public exponent (e) must be provided')
self.e = e
self.n = n
if p and q:
self.p = p
self.q = q
elif n and d:
self.p, self.q = factor_modulus(n, d, e)
else:
raise ValueError('Either (p, q) or (n, d) must be provided')
self._calc_values()
def _calc_values(self):
if self.n is None:
self.n = self.p * self.q
if self.p != self.q:
phi = (self.p - 1) * (self.q - 1)
else:
phi = (self.p ** 2) - self.p
self.d = gmpy.invert(self.e, phi)
# CRT-RSA precomputation
self.dP = self.d % (self.p - 1)
self.dQ = self.d % (self.q - 1)
self.qInv = gmpy.invert(self.q, self.p)
def dump(self):
vars = ['n', 'e', 'd', 'p', 'q', 'dP', 'dQ', 'qInv']
for v in vars:
self._dumpvar(v)
def _dumpvar(self, var):
val = getattr(self, var)
parts = lambda s, l: '\n'.join([s[i:i + l] for i in range(0, len(s), l)])
print(f'{var} = {val} ({val:#x})\n')
if __name__ == '__main__':
parser = optparse.OptionParser()
parser.add_option('-p', dest='p', help='prime', type='int')
parser.add_option('-q', dest='q', help='prime', type='int')
parser.add_option('-n', dest='n', help='modulus', type='int')
parser.add_option('-d', dest='d', help='private exponent', type='int')
parser.add_option('-e', dest='e', help='public exponent', type='int')
try:
(options, args) = parser.parse_args()
if options.n and options.p:
print('Using (n, p) to initialise RSA instance\n')
q = options.n // options.p # Ensure integer division
rsa = RSA(p=options.p, n=options.n, q=q, e=options.e)
elif options.p and options.q:
print('Using (p, q) to initialise RSA instance\n')
rsa = RSA(p=options.p, q=options.q, e=options.e)
elif options.n and options.d:
print('Using (n, d) to initialise RSA instance\n')
rsa = RSA(n=options.n, d=options.d, e=options.e)
else:
parser.print_help()
raise ValueError('Either (p, q) or (n, d) needs to be specified')
print("DUMPING...\n")
rsa.dump()
print("END DUMPING.")
except optparse.OptionValueError as e:
parser.print_help()
raise ValueError(e.msg)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment