using m2crypto to encrypt credit card numbers

Mark McEahern marklists at mceahern.com
Mon Jun 10 18:28:14 EDT 2002


I'm considering using M2Crypto (http://www.post1.com/home/ngps/m2/) to
encrypt credit card numbers.  My part of the problem starts when the credit
card arrives at the web server.  I plan to encrypt it with a public key and
send it to a database that the web server only has write access to.

A separate server will periodically use the private key to read the credit
card data and transmit it to the payment processor.

I've attached a demo below showing the basic outlines of how I plan to use
M2Crypto.  Key management is the part I'm most leery of.  Obviously, I'll
need to:

1.  Generate the key pair.
2.  Store the public key on the web server(s).
3.  Store the private key on the payment server.  Figure out how to unlock
it with the passphrase securely.

I'm interested to hear any and all feedback on the problem and/or solution
space:

1.  What libraries, if any, are you using to encrypt credit card numbers?
2.  How do you manage keys?

Specific technical questions that reveal the abundance of my ignorance:

1.  Do I need to salt the plaintext before I encrypt it or is that what
padding (M2Crypto.RSA.pkcs1_padding) is all about?
2.  Do I need to seed M2Crypto with random data before generating keys -- or
does it take care of that via /dev/[u]random?

Thanks,

// mark

#! /usr/bin/env python
# --------------------------------------------------------------------------
---
# $Id: demo.py,v 1.2 2002/06/10 21:31:20 mmceahern Exp $
# --------------------------------------------------------------------------
---

__doc__ = \
"""
This is just a very quick hack to see if I can understand M2Crypto.

When this runs, you'll be prompted to enter and verify a passphrase.
Use the single letter p (or change passphrase_callback below).  I
haven't figured out how to override the genkey_callback to skip
passphrase prompting--that doesn't sound like a good idea anyway.

The passphrase_callback is for decrypting, which we can override.

I think the use of pkcs1_padding suggests that I may not need to worry
about salt--that may be something that the RSA algorithm(s) in openssl
take care of for us.

I think this shows that I can store the binascii.b2a_hex version of
the binary ciphertext in the database.

Outstanding questions:

1.  Is this the right way to generate a key?
2.  Do I have to explicitly seed the system with random data?
3.  How will we store the passphrase and feed it to the decryption routine?

"""

# --------------------------------------------------------------------------
---
# Third party imports
# --------------------------------------------------------------------------
---
import M2Crypto

# --------------------------------------------------------------------------
---
# Standard library imports
# --------------------------------------------------------------------------
---
import binascii

# --------------------------------------------------------------------------
---
# Helper methods
# --------------------------------------------------------------------------
---
def passphrase_callback(v, prompt1='Enter passphrase:',
                        prompt2='Verify passphrase:'):
    """passphrase_callback(verify, prompt1, prompt2) --> passphrase

    Return the passphrase (we're overriding util.passphrase_callback).
    """
    return "p"

def debug_print(description, text):
    """debug_print(description, text)

    Print the description and text.
    """
    print "<%s>" % description
    print text
    print "</%s>" % description
    print

def generate_key(privkey_file, pubkey_file, key_length):
    e = 65537
    new_key = M2Crypto.RSA.gen_key(key_length, e)
    new_key.save_key(privkey_file)
    new_key.save_pub_key(pubkey_file)

def encrypt(pubkey_file, plaintext):
    """encrypt(pubkey_file, plaintext) --> hexstring

    Encrypt plaintext and return ascii version of ciphertext.
    """
    pub_key = M2Crypto.RSA.load_pub_key(pubkey_file)
    encrypted = pub_key.public_encrypt(plaintext,
M2Crypto.RSA.pkcs1_padding)
    return binascii.b2a_hex(encrypted)

def decrypt(privkey_file, ciphertext):
    """decrypt(privkey_file, ciphertext) --> string

    Decrypt ciphertext (assume hex string) and return the decrypted string.
    """
    encrypted = binascii.a2b_hex(ciphertext)
    priv_key = M2Crypto.RSA.load_key(privkey_file, passphrase_callback)
    decrypted = priv_key.private_decrypt(encrypted,
M2Crypto.RSA.pkcs1_padding)
    return decrypted

def main():
    privkey_file = "priv.pem"
    pubkey_file = "pub.pem"
    key_length = 1024
    generate_key(privkey_file, pubkey_file, key_length)

    plaintext = "5105105105105100"
    encrypted = encrypt(pubkey_file, plaintext)
    decrypted = decrypt(privkey_file, encrypted)

    assert plaintext == decrypted

    debug_print("plain text", plaintext)
    debug_print("cipher text", binascii.b2a_hex(encrypted))
    debug_print("decrypted text", decrypted)

# --------------------------------------------------------------------------
---
# Main
# --------------------------------------------------------------------------
---
if __name__ == "__main__":
    main()

-






More information about the Python-list mailing list