Jan Tattermusch | bb9d788 | 2015-04-22 16:54:59 -0700 | [diff] [blame] | 1 | #region Copyright notice and license |
| 2 | |
| 3 | // Copyright 2015, Google Inc. |
| 4 | // All rights reserved. |
| 5 | // |
| 6 | // Redistribution and use in source and binary forms, with or without |
| 7 | // modification, are permitted provided that the following conditions are |
| 8 | // met: |
| 9 | // |
| 10 | // * Redistributions of source code must retain the above copyright |
| 11 | // notice, this list of conditions and the following disclaimer. |
| 12 | // * Redistributions in binary form must reproduce the above |
| 13 | // copyright notice, this list of conditions and the following disclaimer |
| 14 | // in the documentation and/or other materials provided with the |
| 15 | // distribution. |
| 16 | // * Neither the name of Google Inc. nor the names of its |
| 17 | // contributors may be used to endorse or promote products derived from |
| 18 | // this software without specific prior written permission. |
| 19 | // |
| 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 21 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 22 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 23 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 24 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 25 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 26 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 27 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 28 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 29 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 31 | |
| 32 | #endregion |
| 33 | |
| 34 | using System; |
| 35 | using System.Collections.Generic; |
Jan Tattermusch | bb9d788 | 2015-04-22 16:54:59 -0700 | [diff] [blame] | 36 | using System.IO; |
Jan Tattermusch | 1ca56b9 | 2015-04-27 11:03:06 -0700 | [diff] [blame] | 37 | using System.Security.Cryptography; |
Jan Tattermusch | 7b4a31f | 2015-07-20 17:08:13 -0700 | [diff] [blame] | 38 | using System.Threading; |
| 39 | using System.Threading.Tasks; |
Jan Tattermusch | bb9d788 | 2015-04-22 16:54:59 -0700 | [diff] [blame] | 40 | |
| 41 | using Google.Apis.Auth.OAuth2; |
Jan Tattermusch | 7b4a31f | 2015-07-20 17:08:13 -0700 | [diff] [blame] | 42 | using Google.Apis.Auth.OAuth2.Responses; |
Jan Tattermusch | 1ca56b9 | 2015-04-27 11:03:06 -0700 | [diff] [blame] | 43 | using Newtonsoft.Json.Linq; |
Jan Tattermusch | dca6e88 | 2015-04-22 16:56:27 -0700 | [diff] [blame] | 44 | using Org.BouncyCastle.Crypto.Parameters; |
Jan Tattermusch | dca6e88 | 2015-04-22 16:56:27 -0700 | [diff] [blame] | 45 | using Org.BouncyCastle.Security; |
Jan Tattermusch | bb9d788 | 2015-04-22 16:54:59 -0700 | [diff] [blame] | 46 | |
| 47 | namespace Grpc.Auth |
| 48 | { |
| 49 | // TODO(jtattermusch): Remove this class once possible. |
| 50 | /// <summary> |
| 51 | /// A temporary placeholder for Google credential from |
| 52 | /// Google Auth library for .NET. It emulates the usage pattern |
| 53 | /// for Usable auth. |
| 54 | /// </summary> |
| 55 | public class GoogleCredential |
| 56 | { |
| 57 | private const string GoogleApplicationCredentialsEnvName = "GOOGLE_APPLICATION_CREDENTIALS"; |
Jan Tattermusch | dca6e88 | 2015-04-22 16:56:27 -0700 | [diff] [blame] | 58 | private const string ClientEmailFieldName = "client_email"; |
| 59 | private const string PrivateKeyFieldName = "private_key"; |
Jan Tattermusch | bb9d788 | 2015-04-22 16:54:59 -0700 | [diff] [blame] | 60 | |
| 61 | private ServiceCredential credential; |
| 62 | |
| 63 | private GoogleCredential(ServiceCredential credential) |
| 64 | { |
| 65 | this.credential = credential; |
| 66 | } |
| 67 | |
| 68 | public static GoogleCredential GetApplicationDefault() |
| 69 | { |
| 70 | return new GoogleCredential(null); |
| 71 | } |
| 72 | |
| 73 | public bool IsCreateScopedRequired |
| 74 | { |
| 75 | get |
| 76 | { |
| 77 | return true; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | public GoogleCredential CreateScoped(IEnumerable<string> scopes) |
| 82 | { |
Jan Tattermusch | dca6e88 | 2015-04-22 16:56:27 -0700 | [diff] [blame] | 83 | var credsPath = Environment.GetEnvironmentVariable(GoogleApplicationCredentialsEnvName); |
Jan Tattermusch | 0bbfa38 | 2015-04-27 16:11:59 -0700 | [diff] [blame] | 84 | if (credsPath == null) |
| 85 | { |
| 86 | // Default to ComputeCredentials if path to JSON key is not set. |
| 87 | // ComputeCredential is not scoped actually, but for our use case it's |
| 88 | // fine to treat is as such. |
| 89 | return new GoogleCredential(new ComputeCredential(new ComputeCredential.Initializer())); |
| 90 | } |
Jan Tattermusch | bb9d788 | 2015-04-22 16:54:59 -0700 | [diff] [blame] | 91 | |
Jan Tattermusch | dca6e88 | 2015-04-22 16:56:27 -0700 | [diff] [blame] | 92 | JObject o1 = JObject.Parse(File.ReadAllText(credsPath)); |
| 93 | string clientEmail = o1.GetValue(ClientEmailFieldName).Value<string>(); |
| 94 | string privateKeyString = o1.GetValue(PrivateKeyFieldName).Value<string>(); |
| 95 | var privateKey = ParsePrivateKeyFromString(privateKeyString); |
Jan Tattermusch | bb9d788 | 2015-04-22 16:54:59 -0700 | [diff] [blame] | 96 | |
| 97 | var serviceCredential = new ServiceAccountCredential( |
Jan Tattermusch | dca6e88 | 2015-04-22 16:56:27 -0700 | [diff] [blame] | 98 | new ServiceAccountCredential.Initializer(clientEmail) |
Jan Tattermusch | bb9d788 | 2015-04-22 16:54:59 -0700 | [diff] [blame] | 99 | { |
Jan Tattermusch | dca6e88 | 2015-04-22 16:56:27 -0700 | [diff] [blame] | 100 | Scopes = scopes, |
| 101 | Key = privateKey |
| 102 | }); |
Jan Tattermusch | bb9d788 | 2015-04-22 16:54:59 -0700 | [diff] [blame] | 103 | return new GoogleCredential(serviceCredential); |
| 104 | } |
| 105 | |
Jan Tattermusch | 7b4a31f | 2015-07-20 17:08:13 -0700 | [diff] [blame] | 106 | public Task<bool> RequestAccessTokenAsync(CancellationToken taskCancellationToken) |
| 107 | { |
| 108 | return credential.RequestAccessTokenAsync(taskCancellationToken); |
| 109 | } |
| 110 | |
| 111 | public TokenResponse Token |
| 112 | { |
| 113 | get |
| 114 | { |
| 115 | return credential.Token; |
| 116 | } |
| 117 | } |
| 118 | |
Jan Tattermusch | bb9d788 | 2015-04-22 16:54:59 -0700 | [diff] [blame] | 119 | internal ServiceCredential InternalCredential |
| 120 | { |
| 121 | get |
| 122 | { |
| 123 | return credential; |
| 124 | } |
| 125 | } |
Jan Tattermusch | dca6e88 | 2015-04-22 16:56:27 -0700 | [diff] [blame] | 126 | |
| 127 | private RSACryptoServiceProvider ParsePrivateKeyFromString(string base64PrivateKey) |
| 128 | { |
| 129 | // TODO(jtattermusch): temporary code to create RSACryptoServiceProvider. |
| 130 | base64PrivateKey = base64PrivateKey.Replace("-----BEGIN PRIVATE KEY-----", "").Replace("\n", "").Replace("-----END PRIVATE KEY-----", ""); |
Jan Tattermusch | 818ae16 | 2015-05-07 16:34:44 -0700 | [diff] [blame] | 131 | RsaPrivateCrtKeyParameters key = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(base64PrivateKey)); |
Jan Tattermusch | dca6e88 | 2015-04-22 16:56:27 -0700 | [diff] [blame] | 132 | RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(key); |
| 133 | RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); |
| 134 | rsa.ImportParameters(rsaParameters); |
| 135 | return rsa; |
| 136 | } |
Jan Tattermusch | bb9d788 | 2015-04-22 16:54:59 -0700 | [diff] [blame] | 137 | } |
| 138 | } |