keyfactory.py revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
1"""Factory functions for asymmetric cryptography.
2@sort: generateRSAKey, parseXMLKey, parsePEMKey, parseAsPublicKey,
3parseAsPrivateKey
4"""
5
6from compat import *
7
8from rsakey import RSAKey
9from python_rsakey import Python_RSAKey
10import cryptomath
11
12if cryptomath.m2cryptoLoaded:
13    from openssl_rsakey import OpenSSL_RSAKey
14
15if cryptomath.pycryptoLoaded:
16    from pycrypto_rsakey import PyCrypto_RSAKey
17
18# **************************************************************************
19# Factory Functions for RSA Keys
20# **************************************************************************
21
22def generateRSAKey(bits, implementations=["openssl", "python"]):
23    """Generate an RSA key with the specified bit length.
24
25    @type bits: int
26    @param bits: Desired bit length of the new key's modulus.
27
28    @rtype: L{tlslite.utils.RSAKey.RSAKey}
29    @return: A new RSA private key.
30    """
31    for implementation in implementations:
32        if implementation == "openssl" and cryptomath.m2cryptoLoaded:
33            return OpenSSL_RSAKey.generate(bits)
34        elif implementation == "python":
35            return Python_RSAKey.generate(bits)
36    raise ValueError("No acceptable implementations")
37
38def parseXMLKey(s, private=False, public=False, implementations=["python"]):
39    """Parse an XML-format key.
40
41    The XML format used here is specific to tlslite and cryptoIDlib.  The
42    format can store the public component of a key, or the public and
43    private components.  For example::
44
45        <publicKey xmlns="http://trevp.net/rsa">
46            <n>4a5yzB8oGNlHo866CAspAC47M4Fvx58zwK8pou...
47            <e>Aw==</e>
48        </publicKey>
49
50        <privateKey xmlns="http://trevp.net/rsa">
51            <n>4a5yzB8oGNlHo866CAspAC47M4Fvx58zwK8pou...
52            <e>Aw==</e>
53            <d>JZ0TIgUxWXmL8KJ0VqyG1V0J3ern9pqIoB0xmy...
54            <p>5PreIj6z6ldIGL1V4+1C36dQFHNCQHJvW52GXc...
55            <q>/E/wDit8YXPCxx126zTq2ilQ3IcW54NJYyNjiZ...
56            <dP>mKc+wX8inDowEH45Qp4slRo1YveBgExKPROu6...
57            <dQ>qDVKtBz9lk0shL5PR3ickXDgkwS576zbl2ztB...
58            <qInv>j6E8EA7dNsTImaXexAmLA1DoeArsYeFAInr...
59        </privateKey>
60
61    @type s: str
62    @param s: A string containing an XML public or private key.
63
64    @type private: bool
65    @param private: If True, a L{SyntaxError} will be raised if the private
66    key component is not present.
67
68    @type public: bool
69    @param public: If True, the private key component (if present) will be
70    discarded, so this function will always return a public key.
71
72    @rtype: L{tlslite.utils.RSAKey.RSAKey}
73    @return: An RSA key.
74
75    @raise SyntaxError: If the key is not properly formatted.
76    """
77    for implementation in implementations:
78        if implementation == "python":
79            key = Python_RSAKey.parseXML(s)
80            break
81    else:
82        raise ValueError("No acceptable implementations")
83
84    return _parseKeyHelper(key, private, public)
85
86#Parse as an OpenSSL or Python key
87def parsePEMKey(s, private=False, public=False, passwordCallback=None,
88                implementations=["openssl", "python"]):
89    """Parse a PEM-format key.
90
91    The PEM format is used by OpenSSL and other tools.  The
92    format is typically used to store both the public and private
93    components of a key.  For example::
94
95       -----BEGIN RSA PRIVATE KEY-----
96        MIICXQIBAAKBgQDYscuoMzsGmW0pAYsmyHltxB2TdwHS0dImfjCMfaSDkfLdZY5+
97        dOWORVns9etWnr194mSGA1F0Pls/VJW8+cX9+3vtJV8zSdANPYUoQf0TP7VlJxkH
98        dSRkUbEoz5bAAs/+970uos7n7iXQIni+3erUTdYEk2iWnMBjTljfgbK/dQIDAQAB
99        AoGAJHoJZk75aKr7DSQNYIHuruOMdv5ZeDuJvKERWxTrVJqE32/xBKh42/IgqRrc
100        esBN9ZregRCd7YtxoL+EVUNWaJNVx2mNmezEznrc9zhcYUrgeaVdFO2yBF1889zO
101        gCOVwrO8uDgeyj6IKa25H6c1N13ih/o7ZzEgWbGG+ylU1yECQQDv4ZSJ4EjSh/Fl
102        aHdz3wbBa/HKGTjC8iRy476Cyg2Fm8MZUe9Yy3udOrb5ZnS2MTpIXt5AF3h2TfYV
103        VoFXIorjAkEA50FcJmzT8sNMrPaV8vn+9W2Lu4U7C+K/O2g1iXMaZms5PC5zV5aV
104        CKXZWUX1fq2RaOzlbQrpgiolhXpeh8FjxwJBAOFHzSQfSsTNfttp3KUpU0LbiVvv
105        i+spVSnA0O4rq79KpVNmK44Mq67hsW1P11QzrzTAQ6GVaUBRv0YS061td1kCQHnP
106        wtN2tboFR6lABkJDjxoGRvlSt4SOPr7zKGgrWjeiuTZLHXSAnCY+/hr5L9Q3ZwXG
107        6x6iBdgLjVIe4BZQNtcCQQDXGv/gWinCNTN3MPWfTW/RGzuMYVmyBFais0/VrgdH
108        h1dLpztmpQqfyH/zrBXQ9qL/zR4ojS6XYneO/U18WpEe
109        -----END RSA PRIVATE KEY-----
110
111    To generate a key like this with OpenSSL, run::
112
113        openssl genrsa 2048 > key.pem
114
115    This format also supports password-encrypted private keys.  TLS
116    Lite can only handle password-encrypted private keys when OpenSSL
117    and M2Crypto are installed.  In this case, passwordCallback will be
118    invoked to query the user for the password.
119
120    @type s: str
121    @param s: A string containing a PEM-encoded public or private key.
122
123    @type private: bool
124    @param private: If True, a L{SyntaxError} will be raised if the
125    private key component is not present.
126
127    @type public: bool
128    @param public: If True, the private key component (if present) will
129    be discarded, so this function will always return a public key.
130
131    @type passwordCallback: callable
132    @param passwordCallback: This function will be called, with no
133    arguments, if the PEM-encoded private key is password-encrypted.
134    The callback should return the password string.  If the password is
135    incorrect, SyntaxError will be raised.  If no callback is passed
136    and the key is password-encrypted, a prompt will be displayed at
137    the console.
138
139    @rtype: L{tlslite.utils.RSAKey.RSAKey}
140    @return: An RSA key.
141
142    @raise SyntaxError: If the key is not properly formatted.
143    """
144    for implementation in implementations:
145        if implementation == "openssl" and cryptomath.m2cryptoLoaded:
146            key = OpenSSL_RSAKey.parse(s, passwordCallback)
147            break
148        elif implementation == "python":
149            key = Python_RSAKey.parsePEM(s)
150            break
151    else:
152        raise ValueError("No acceptable implementations")
153
154    return _parseKeyHelper(key, private, public)
155
156
157def _parseKeyHelper(key, private, public):
158    if private:
159        if not key.hasPrivateKey():
160            raise SyntaxError("Not a private key!")
161
162    if public:
163        return _createPublicKey(key)
164
165    if private:
166        if hasattr(key, "d"):
167            return _createPrivateKey(key)
168        else:
169            return key
170
171    return key
172
173def parseAsPublicKey(s):
174    """Parse an XML or PEM-formatted public key.
175
176    @type s: str
177    @param s: A string containing an XML or PEM-encoded public or private key.
178
179    @rtype: L{tlslite.utils.RSAKey.RSAKey}
180    @return: An RSA public key.
181
182    @raise SyntaxError: If the key is not properly formatted.
183    """
184    try:
185        return parsePEMKey(s, public=True)
186    except:
187        return parseXMLKey(s, public=True)
188
189def parsePrivateKey(s):
190    """Parse an XML or PEM-formatted private key.
191
192    @type s: str
193    @param s: A string containing an XML or PEM-encoded private key.
194
195    @rtype: L{tlslite.utils.RSAKey.RSAKey}
196    @return: An RSA private key.
197
198    @raise SyntaxError: If the key is not properly formatted.
199    """
200    try:
201        return parsePEMKey(s, private=True)
202    except:
203        return parseXMLKey(s, private=True)
204
205def _createPublicKey(key):
206    """
207    Create a new public key.  Discard any private component,
208    and return the most efficient key possible.
209    """
210    if not isinstance(key, RSAKey):
211        raise AssertionError()
212    return _createPublicRSAKey(key.n, key.e)
213
214def _createPrivateKey(key):
215    """
216    Create a new private key.  Return the most efficient key possible.
217    """
218    if not isinstance(key, RSAKey):
219        raise AssertionError()
220    if not key.hasPrivateKey():
221        raise AssertionError()
222    return _createPrivateRSAKey(key.n, key.e, key.d, key.p, key.q, key.dP,
223                                key.dQ, key.qInv)
224
225def _createPublicRSAKey(n, e, implementations = ["openssl", "pycrypto",
226                                                "python"]):
227    for implementation in implementations:
228        if implementation == "openssl" and cryptomath.m2cryptoLoaded:
229            return OpenSSL_RSAKey(n, e)
230        elif implementation == "pycrypto" and cryptomath.pycryptoLoaded:
231            return PyCrypto_RSAKey(n, e)
232        elif implementation == "python":
233            return Python_RSAKey(n, e)
234    raise ValueError("No acceptable implementations")
235
236def _createPrivateRSAKey(n, e, d, p, q, dP, dQ, qInv,
237                        implementations = ["pycrypto", "python"]):
238    for implementation in implementations:
239        if implementation == "pycrypto" and cryptomath.pycryptoLoaded:
240            return PyCrypto_RSAKey(n, e, d, p, q, dP, dQ, qInv)
241        elif implementation == "python":
242            return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv)
243    raise ValueError("No acceptable implementations")
244