• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015 Google Inc. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""pyCrypto Crypto-related routines for oauth2client."""
15
16from Crypto.PublicKey import RSA
17from Crypto.Hash import SHA256
18from Crypto.Signature import PKCS1_v1_5
19from Crypto.Util.asn1 import DerSequence
20
21from oauth2client._helpers import _parse_pem_key
22from oauth2client._helpers import _to_bytes
23from oauth2client._helpers import _urlsafe_b64decode
24
25
26class PyCryptoVerifier(object):
27    """Verifies the signature on a message."""
28
29    def __init__(self, pubkey):
30        """Constructor.
31
32        Args:
33            pubkey: OpenSSL.crypto.PKey (or equiv), The public key to verify
34            with.
35        """
36        self._pubkey = pubkey
37
38    def verify(self, message, signature):
39        """Verifies a message against a signature.
40
41        Args:
42            message: string or bytes, The message to verify. If string, will be
43                     encoded to bytes as utf-8.
44            signature: string or bytes, The signature on the message.
45
46        Returns:
47            True if message was signed by the private key associated with the
48            public key that this object was constructed with.
49        """
50        message = _to_bytes(message, encoding='utf-8')
51        return PKCS1_v1_5.new(self._pubkey).verify(
52            SHA256.new(message), signature)
53
54    @staticmethod
55    def from_string(key_pem, is_x509_cert):
56        """Construct a Verified instance from a string.
57
58        Args:
59            key_pem: string, public key in PEM format.
60            is_x509_cert: bool, True if key_pem is an X509 cert, otherwise it
61                          is expected to be an RSA key in PEM format.
62
63        Returns:
64            Verifier instance.
65        """
66        if is_x509_cert:
67            key_pem = _to_bytes(key_pem)
68            pemLines = key_pem.replace(b' ', b'').split()
69            certDer = _urlsafe_b64decode(b''.join(pemLines[1:-1]))
70            certSeq = DerSequence()
71            certSeq.decode(certDer)
72            tbsSeq = DerSequence()
73            tbsSeq.decode(certSeq[0])
74            pubkey = RSA.importKey(tbsSeq[6])
75        else:
76            pubkey = RSA.importKey(key_pem)
77        return PyCryptoVerifier(pubkey)
78
79
80class PyCryptoSigner(object):
81    """Signs messages with a private key."""
82
83    def __init__(self, pkey):
84        """Constructor.
85
86        Args:
87            pkey, OpenSSL.crypto.PKey (or equiv), The private key to sign with.
88        """
89        self._key = pkey
90
91    def sign(self, message):
92        """Signs a message.
93
94        Args:
95            message: string, Message to be signed.
96
97        Returns:
98            string, The signature of the message for the given key.
99        """
100        message = _to_bytes(message, encoding='utf-8')
101        return PKCS1_v1_5.new(self._key).sign(SHA256.new(message))
102
103    @staticmethod
104    def from_string(key, password='notasecret'):
105        """Construct a Signer instance from a string.
106
107        Args:
108            key: string, private key in PEM format.
109            password: string, password for private key file. Unused for PEM
110                      files.
111
112        Returns:
113            Signer instance.
114
115        Raises:
116            NotImplementedError if the key isn't in PEM format.
117        """
118        parsed_pem_key = _parse_pem_key(key)
119        if parsed_pem_key:
120            pkey = RSA.importKey(parsed_pem_key)
121        else:
122            raise NotImplementedError(
123                'PKCS12 format is not supported by the PyCrypto library. '
124                'Try converting to a "PEM" '
125                '(openssl pkcs12 -in xxxxx.p12 -nodes -nocerts > '
126                'privatekey.pem) '
127                'or using PyOpenSSL if native code is an option.')
128        return PyCryptoSigner(pkey)
129