Private/Public Encryption in Python with Standard Library
Asked Answered
H

4

24

Is there a module that has my searching has been unable to discover that would allow writing code like the following? The reason for wanting to write code like this is unimportant. All I am after is some code that has a simple API to generate public and private byte keys and to easily encode and decode data with those keys.

import module, os

method, bits, data = 'RSA', 1024, os.urandom(1024)
public, private = module.generate_keys(method, bits)

assert isinstance(public, bytes) and isinstance(private, bytes)
assert module.decode(module.encode(data, private), public) == data
assert module.decode(module.encode(data, public), private) == data

Most of what appears to be available requires downloading a package and only runs on Python 2.x. It is also quite common to find libraries that work with PEM files or other types of certificates. I would like to avoid having to deal with such files, to generate public and private keys on the fly, and quickly work with data in memory.

Histrionic answered 16/12, 2011 at 19:56 Comment(1)
I don't know of an ideal solution, but you could always fall back on using python subprocess module to invoke gpg via command linePasty
L
38

Public key encryption is not in the standard library. There are some third party libraries on PyPi for it though:

If you're interested in the math behind it, Python makes it easy to experiment:

code = pow(msg, 65537, 5551201688147)               # encode using a public key
plaintext = pow(code, 109182490673, 5551201688147)  # decode using a private key

The key generation is a little more involved. Here is a simplified example of how to do key generation in-memory using urandom as the source of entropy. The code runs under both Py2.6 and Py3.x:

import random

def gen_prime(N=10**8, bases=range(2,20000)):
    # XXX replace with a more sophisticated algorithm
    p = 1
    while any(pow(base, p-1, p) != 1 for base in bases):
        p = random.SystemRandom().randrange(N)
    return p

def multinv(modulus, value):
    '''Multiplicative inverse in a given modulus

        >>> multinv(191, 138)
        18
        >>> 18 * 138 % 191
        1

    '''
    # http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
    x, lastx = 0, 1
    a, b = modulus, value
    while b:
        a, q, b = b, a // b, a % b
        x, lastx = lastx - q * x, x
    result = (1 - lastx * modulus) // value
    return result + modulus if result < 0 else result

def keygen(N):
    '''Generate public and private keys from primes up to N.

        >>> pubkey, privkey = keygen(2**64)
        >>> msg = 123456789012345
        >>> coded = pow(msg, 65537, pubkey)
        >>> plain = pow(coded, privkey, pubkey)
        >>> assert msg == plain

    '''
    # http://en.wikipedia.org/wiki/RSA
    prime1 = gen_prime(N)
    prime2 = gen_prime(N)
    totient = (prime1 - 1) * (prime2 - 1)
    return prime1 * prime2, multinv(totient, 65537)
Lilalilac answered 16/12, 2011 at 19:59 Comment(8)
Do you know if any of those libraries support both a simple API like the one shown above and runs on Python 3.x?Histrionic
The RSA Python link has pure python code including much of what you're looking for. You will likely have to adapt it a bit to exactly match the API you're looking for. The APSN recipes, pow examples, and PyCrypto work fine on Python 3.Lilalilac
Hi @RaymondHettinger . I wanted to implement this algo in java as you described here. But i see that what python easily does with pow(code,pub,pri) is nearly impossible to calculate with java. I think i'm missing sth. Would you suggest me sth? (even not, thank you for the answer :) )Irritable
Hi @RaymondHettinger the point that I'm missing is modular exponentiation. Python implements it natively. Thus calculation is really fast even in RPi. Thanks again.Irritable
@MertGülsoy Fast modular exponent computations are easy to implement. See math.stackexchange.com/a/453108Lilalilac
Please note the Python code shown here is wildly insufficient to securely encrypt and decrypt data. Using it for learning is fine, but don't expect the encrypted values to be secured. As always, don't roll your own crypto.Januaryjanuisz
@Januaryjanuisz Please re-read the post. I gave links to "pre-rolled" libraries. The code it itself was prefaced with "If you're interested in the math behind it, Python makes it easy to experiment". I also wrote "here is a simplified example". I think that was pretty clear. Your comment impugned the answer as if there were something wrong with it. IMO, that isn't doing a service to the readers of the post -- it just serves as a discouragement from ever giving an answer that helps people understand how an algorithm works.Lilalilac
@RaymondHettinger I understand completely, and I'm sorry for it, but at no point in your post you explicitly said "this is insecure", or even hinted that the missing parts are important to security. Without this disclaimer, people are going to come here, copy the code, notice the results look garbled enough, and proceed to shoot themselves in the foot by using it somewhere important. I'm not comfortable editing your post, but if you insert a disclaimer I'll happily delete my comment.Januaryjanuisz
P
3

PyCrypto works on Python 3 as of 2.4.1.

Peroxide answered 16/12, 2011 at 20:10 Comment(0)
D
2

Here's another example

import random


# RSA Algorithm



ops = raw_input('Would you like a list of prime numbers to choose from (y/n)? ')
op = ops.upper()

if op == 'Y':
    print """\n 2      3      5      7     11     13     17     19     23     29 
31     37     41     43     47     53     59     61     67     71 
73     79     83     89     97    101    103    107    109    113 
127    131    137    139    149    151    157    163    167    173 
179    181    191    193    197    199    211    223    227    229 
233    239    241    251    257    263    269    271    277    281 
283    293    307    311    313    317    331    337    347    349 
353    359    367    373    379    383    389    397    401    409 
419    421    431    433    439    443    449    457    461    463 
467    479    487    491    499    503    509    521    523    541 
547    557    563    569    571    577    587    593    599 \n"""
    rsa()
else:
    print "\n"
    rsa()

def rsa():
    # Choose two prime numbers p and q
    p = raw_input('Choose a p: ')
    p = int(p)

while isPrime(p) == False:
    print "Please ensure p is prime"
    p = raw_input('Choose a p: ')
    p = int(p)

q = raw_input('Choose a q: ')
q = int(q)

while isPrime(q) == False or p==q:
    print "Please ensure q is prime and NOT the same value as p"
    q = raw_input('Choose a q: ')
    q = int(q)

# Compute n = pq
n = p * q

# Compute the phi of n
phi = (p-1) * (q-1)

# Choose an integer e such that e and phi(n) are coprime
e = random.randrange(1,phi)

# Use Euclid's Algorithm to verify that e and phi(n) are comprime
g = euclid(e,phi)
while(g!=1):
    e = random.randrange(1,phi)
    g = euclid(e,phi)

# Use Extended Euclid's Algorithm 
d = extended_euclid(e,phi)

# Public and Private Key have been generated
public_key=(e,n)
private_key=(d,n)
print "Public Key [E,N]: ", public_key
print "Private Key [D,N]: ", private_key

# Enter plain text to be encrypted using the Public Key
sentence = raw_input('Enter plain text: ')
letters = list(sentence)

cipher = []
num = ""

# Encrypt the plain text
for i in range(0,len(letters)):
    print "Value of ", letters[i], " is ", character[letters[i]]

    c = (character[letters[i]]**e)%n
    cipher += [c]
    num += str(c)
print "Cipher Text is: ", num

plain = []
sentence = ""

# Decrypt the cipher text    
for j in range(0,len(cipher)):

    p = (cipher[j]**d)%n

    for key in character.keys():
        if character[key]==p:
            plain += [key]
            sentence += key
            break
print "Plain Text is: ", sentence

# Euclid's Algorithm
def euclid(a, b):
   if b==0:
   return a
else:
   return euclid(b, a % b)

# Euclid's Extended Algorithm
def extended_euclid(e,phi):
    d=0
    x1=0
    x2=1
    y1=1
    orig_phi = phi
    tempPhi = phi

while (e>0):
  temp1 = int(tempPhi/e)
  temp2 = tempPhi - temp1 * e
  tempPhi = e
  e = temp2

  x = x2- temp1* x1
  y = d - temp1 * y1

  x2 = x1
  x1 = x
  d = y1
  y1 = y

  if tempPhi == 1:
      d += phi
      break
return d

# Checks if n is a prime number
def isPrime(n):
   for i in range(2,n):
    if n%i == 0:
        return False
return True

character = {"A":1,"B":2,"C":3,"D":4,"E":5,"F":6,"G":7,"H":8,"I":9,"J":10,
     "K":11,"L":12,"M":13,"N":14,"O":15,"P":16,"Q":17,"R":18,"S":19,
     "T":20,"U":21,"V":22,"W":23,"X":24,"Y":25,"Z":26,"a":27,"b":28,
     "c":29,"d":30,"e":31,"f":32,"g":33,"h":34,"i":35,"j":36,"k":37,
     "l":38,"m":39,"n":40,"o":41,"p":42,"q":43,"r":44,"s":45,"t":46,
     "u":47,"v":48,"w":49,"x":50,"y":51,"z":52, " ":53, ".":54, ",":55,
     "?":56,"/":57,"!":58,"(":59,")":60,"$":61,":":62,";":63,"'":64,"@":65,
     "#":66,"%":67,"^":68,"&":69,"*":70,"+":71,"-":72,"_":73,"=":74}
Discordance answered 1/4, 2013 at 18:55 Comment(1)
This code has not any chance to work. Indentation is broken. Besides is very hard to read (variables names)Apprehension
T
2

In various points PyCrypto is mentioned here. With this answer, instead of a comment, I would like to point out that the last release of PyCrypto is from 2013. For a cryptography library, this is unacceptable, as potentially dangerous issues are not fixed.

However, there is a fork of PyCrypto, PyCryptodome, which will continue to be developed. Link to Github: https://github.com/Legrandin/pycryptodome

Talkathon answered 29/4, 2021 at 10:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.