Another solution is to use public-key cryptography. In a public key system, there are two different keys: one for encryption and one for decryption. The encryption key can be made public by listing it in a directory or mailing it to your correspondent, while you keep the decryption key secret. Your correspondent then sends you data encrypted with your public key, and you use the private key to decrypt it. While the two keys are related, it's very difficult to derive the private key given only the public key; however, deriving the private key is always possible given enough time and computing power. This makes it very important to pick keys of the right size: large enough to be secure, but small enough to be applied fairly quickly.
Many public-key algorithms can also be used to sign messages; simply run the message to be signed through a decryption with your private key key. Anyone receiving the message can encrypt it with your publicly available key and read the message. Some algorithms do only one thing, others can both encrypt and authenticate.
The currently available public-key algorithms are listed in the following table:
Algorithm | Capabilities |
---|---|
RSA | Encryption, authentication/signatures |
ElGamal | Encryption, authentication/signatures |
DSA | Authentication/signatures |
qNEW | Authentication/signatures |
Many of these algorithms are patented. Before using any of them in a commercial product, consult a patent attorney; you may have to arrange a license with the patent holder.
An example of using the RSA module to sign a message:
>>> from Crypto.Hash import MD5 >>> from Crypto.PublicKey import RSA >>> RSAkey = RSA.generate(384, randfunc) # This will take a while... >>> hash = MD5.new(plaintext).digest() >>> signature = RSAkey.sign(hash, "") >>> signature # Print what an RSA sig looks like--you don't really care. ('\021\317\313\336\264\315' ...,) >>> RSAkey.verify(hash, signature) # This sig will check out 1 >>> RSAkey.verify(hash[:-1], signature)# This sig will fail 0
Public-key modules make the following functions available:
None
)
randfunc is a random number generation function; it should accept a single integer N and return a string of random data N bytes long. You should always use a cryptographically secure random number generator, such as the one defined in the Crypto.Util.randpool module; don't just use the current time and the random module.
progress_func is an optional function that will be called with a short string containing the key parameter currently being generated; it's useful for interactive applications where a user is waiting for a key to be generated.
If you want to interface with some other program, you will have to know the details of the algorithm being used; this isn't a big loss. If you don't care about working with non-Python software, simply use the pickle module when you need to write a key or a signature to a file. It's portable across all the architectures that Python supports, and it's simple to use.
Public-key objects always support the following methods. Some of them may raise exceptions if their functionality is not supported by the algorithm.
key.can_encrypt() and key.has_private()
.
key.can_sign() and key.has_private()
.
self.p-1
; an
exception is raised if it is not.
sign()
raises an
exception if string is too long. For ElGamal objects, the value
of K expressed as a big-endian integer must be relatively prime to
self.p-1
; an exception is raised if it is not.
verify
does
not run a hash function over the data, but you can easily do that yourself.