summaryrefslogtreecommitdiffstats
path: root/src/lib/tlslite/utils/keyfactory.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/tlslite/utils/keyfactory.py')
-rwxr-xr-xsrc/lib/tlslite/utils/keyfactory.py243
1 files changed, 243 insertions, 0 deletions
diff --git a/src/lib/tlslite/utils/keyfactory.py b/src/lib/tlslite/utils/keyfactory.py
new file mode 100755
index 000000000..5005af7f5
--- /dev/null
+++ b/src/lib/tlslite/utils/keyfactory.py
@@ -0,0 +1,243 @@
+"""Factory functions for asymmetric cryptography.
+@sort: generateRSAKey, parseXMLKey, parsePEMKey, parseAsPublicKey,
+parseAsPrivateKey
+"""
+
+from compat import *
+
+from RSAKey import RSAKey
+from Python_RSAKey import Python_RSAKey
+import cryptomath
+
+if cryptomath.m2cryptoLoaded:
+ from OpenSSL_RSAKey import OpenSSL_RSAKey
+
+if cryptomath.pycryptoLoaded:
+ from PyCrypto_RSAKey import PyCrypto_RSAKey
+
+# **************************************************************************
+# Factory Functions for RSA Keys
+# **************************************************************************
+
+def generateRSAKey(bits, implementations=["openssl", "python"]):
+ """Generate an RSA key with the specified bit length.
+
+ @type bits: int
+ @param bits: Desired bit length of the new key's modulus.
+
+ @rtype: L{tlslite.utils.RSAKey.RSAKey}
+ @return: A new RSA private key.
+ """
+ for implementation in implementations:
+ if implementation == "openssl" and cryptomath.m2cryptoLoaded:
+ return OpenSSL_RSAKey.generate(bits)
+ elif implementation == "python":
+ return Python_RSAKey.generate(bits)
+ raise ValueError("No acceptable implementations")
+
+def parseXMLKey(s, private=False, public=False, implementations=["python"]):
+ """Parse an XML-format key.
+
+ The XML format used here is specific to tlslite and cryptoIDlib. The
+ format can store the public component of a key, or the public and
+ private components. For example::
+
+ <publicKey xmlns="http://trevp.net/rsa">
+ <n>4a5yzB8oGNlHo866CAspAC47M4Fvx58zwK8pou...
+ <e>Aw==</e>
+ </publicKey>
+
+ <privateKey xmlns="http://trevp.net/rsa">
+ <n>4a5yzB8oGNlHo866CAspAC47M4Fvx58zwK8pou...
+ <e>Aw==</e>
+ <d>JZ0TIgUxWXmL8KJ0VqyG1V0J3ern9pqIoB0xmy...
+ <p>5PreIj6z6ldIGL1V4+1C36dQFHNCQHJvW52GXc...
+ <q>/E/wDit8YXPCxx126zTq2ilQ3IcW54NJYyNjiZ...
+ <dP>mKc+wX8inDowEH45Qp4slRo1YveBgExKPROu6...
+ <dQ>qDVKtBz9lk0shL5PR3ickXDgkwS576zbl2ztB...
+ <qInv>j6E8EA7dNsTImaXexAmLA1DoeArsYeFAInr...
+ </privateKey>
+
+ @type s: str
+ @param s: A string containing an XML public or private key.
+
+ @type private: bool
+ @param private: If True, a L{SyntaxError} will be raised if the private
+ key component is not present.
+
+ @type public: bool
+ @param public: If True, the private key component (if present) will be
+ discarded, so this function will always return a public key.
+
+ @rtype: L{tlslite.utils.RSAKey.RSAKey}
+ @return: An RSA key.
+
+ @raise SyntaxError: If the key is not properly formatted.
+ """
+ for implementation in implementations:
+ if implementation == "python":
+ key = Python_RSAKey.parseXML(s)
+ break
+ else:
+ raise ValueError("No acceptable implementations")
+
+ return _parseKeyHelper(key, private, public)
+
+#Parse as an OpenSSL or Python key
+def parsePEMKey(s, private=False, public=False, passwordCallback=None,
+ implementations=["openssl", "python"]):
+ """Parse a PEM-format key.
+
+ The PEM format is used by OpenSSL and other tools. The
+ format is typically used to store both the public and private
+ components of a key. For example::
+
+ -----BEGIN RSA PRIVATE KEY-----
+ MIICXQIBAAKBgQDYscuoMzsGmW0pAYsmyHltxB2TdwHS0dImfjCMfaSDkfLdZY5+
+ dOWORVns9etWnr194mSGA1F0Pls/VJW8+cX9+3vtJV8zSdANPYUoQf0TP7VlJxkH
+ dSRkUbEoz5bAAs/+970uos7n7iXQIni+3erUTdYEk2iWnMBjTljfgbK/dQIDAQAB
+ AoGAJHoJZk75aKr7DSQNYIHuruOMdv5ZeDuJvKERWxTrVJqE32/xBKh42/IgqRrc
+ esBN9ZregRCd7YtxoL+EVUNWaJNVx2mNmezEznrc9zhcYUrgeaVdFO2yBF1889zO
+ gCOVwrO8uDgeyj6IKa25H6c1N13ih/o7ZzEgWbGG+ylU1yECQQDv4ZSJ4EjSh/Fl
+ aHdz3wbBa/HKGTjC8iRy476Cyg2Fm8MZUe9Yy3udOrb5ZnS2MTpIXt5AF3h2TfYV
+ VoFXIorjAkEA50FcJmzT8sNMrPaV8vn+9W2Lu4U7C+K/O2g1iXMaZms5PC5zV5aV
+ CKXZWUX1fq2RaOzlbQrpgiolhXpeh8FjxwJBAOFHzSQfSsTNfttp3KUpU0LbiVvv
+ i+spVSnA0O4rq79KpVNmK44Mq67hsW1P11QzrzTAQ6GVaUBRv0YS061td1kCQHnP
+ wtN2tboFR6lABkJDjxoGRvlSt4SOPr7zKGgrWjeiuTZLHXSAnCY+/hr5L9Q3ZwXG
+ 6x6iBdgLjVIe4BZQNtcCQQDXGv/gWinCNTN3MPWfTW/RGzuMYVmyBFais0/VrgdH
+ h1dLpztmpQqfyH/zrBXQ9qL/zR4ojS6XYneO/U18WpEe
+ -----END RSA PRIVATE KEY-----
+
+ To generate a key like this with OpenSSL, run::
+
+ openssl genrsa 2048 > key.pem
+
+ This format also supports password-encrypted private keys. TLS
+ Lite can only handle password-encrypted private keys when OpenSSL
+ and M2Crypto are installed. In this case, passwordCallback will be
+ invoked to query the user for the password.
+
+ @type s: str
+ @param s: A string containing a PEM-encoded public or private key.
+
+ @type private: bool
+ @param private: If True, a L{SyntaxError} will be raised if the
+ private key component is not present.
+
+ @type public: bool
+ @param public: If True, the private key component (if present) will
+ be discarded, so this function will always return a public key.
+
+ @type passwordCallback: callable
+ @param passwordCallback: This function will be called, with no
+ arguments, if the PEM-encoded private key is password-encrypted.
+ The callback should return the password string. If the password is
+ incorrect, SyntaxError will be raised. If no callback is passed
+ and the key is password-encrypted, a prompt will be displayed at
+ the console.
+
+ @rtype: L{tlslite.utils.RSAKey.RSAKey}
+ @return: An RSA key.
+
+ @raise SyntaxError: If the key is not properly formatted.
+ """
+ for implementation in implementations:
+ if implementation == "openssl" and cryptomath.m2cryptoLoaded:
+ key = OpenSSL_RSAKey.parse(s, passwordCallback)
+ break
+ elif implementation == "python":
+ key = Python_RSAKey.parsePEM(s)
+ break
+ else:
+ raise ValueError("No acceptable implementations")
+
+ return _parseKeyHelper(key, private, public)
+
+
+def _parseKeyHelper(key, private, public):
+ if private:
+ if not key.hasPrivateKey():
+ raise SyntaxError("Not a private key!")
+
+ if public:
+ return _createPublicKey(key)
+
+ if private:
+ if hasattr(key, "d"):
+ return _createPrivateKey(key)
+ else:
+ return key
+
+ return key
+
+def parseAsPublicKey(s):
+ """Parse an XML or PEM-formatted public key.
+
+ @type s: str
+ @param s: A string containing an XML or PEM-encoded public or private key.
+
+ @rtype: L{tlslite.utils.RSAKey.RSAKey}
+ @return: An RSA public key.
+
+ @raise SyntaxError: If the key is not properly formatted.
+ """
+ try:
+ return parsePEMKey(s, public=True)
+ except:
+ return parseXMLKey(s, public=True)
+
+def parsePrivateKey(s):
+ """Parse an XML or PEM-formatted private key.
+
+ @type s: str
+ @param s: A string containing an XML or PEM-encoded private key.
+
+ @rtype: L{tlslite.utils.RSAKey.RSAKey}
+ @return: An RSA private key.
+
+ @raise SyntaxError: If the key is not properly formatted.
+ """
+ try:
+ return parsePEMKey(s, private=True)
+ except:
+ return parseXMLKey(s, private=True)
+
+def _createPublicKey(key):
+ """
+ Create a new public key. Discard any private component,
+ and return the most efficient key possible.
+ """
+ if not isinstance(key, RSAKey):
+ raise AssertionError()
+ return _createPublicRSAKey(key.n, key.e)
+
+def _createPrivateKey(key):
+ """
+ Create a new private key. Return the most efficient key possible.
+ """
+ if not isinstance(key, RSAKey):
+ raise AssertionError()
+ if not key.hasPrivateKey():
+ raise AssertionError()
+ return _createPrivateRSAKey(key.n, key.e, key.d, key.p, key.q, key.dP,
+ key.dQ, key.qInv)
+
+def _createPublicRSAKey(n, e, implementations = ["openssl", "pycrypto",
+ "python"]):
+ for implementation in implementations:
+ if implementation == "openssl" and cryptomath.m2cryptoLoaded:
+ return OpenSSL_RSAKey(n, e)
+ elif implementation == "pycrypto" and cryptomath.pycryptoLoaded:
+ return PyCrypto_RSAKey(n, e)
+ elif implementation == "python":
+ return Python_RSAKey(n, e)
+ raise ValueError("No acceptable implementations")
+
+def _createPrivateRSAKey(n, e, d, p, q, dP, dQ, qInv,
+ implementations = ["pycrypto", "python"]):
+ for implementation in implementations:
+ if implementation == "pycrypto" and cryptomath.pycryptoLoaded:
+ return PyCrypto_RSAKey(n, e, d, p, q, dP, dQ, qInv)
+ elif implementation == "python":
+ return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv)
+ raise ValueError("No acceptable implementations")