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