Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NetStandard improvements #806

Merged
merged 2 commits into from
Aug 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
<HintPath>..\packages\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Moq, Version=4.2.1402.2112, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\packages\Moq.4.2.1402.2112\lib\net40\Moq.dll</HintPath>
<Private>True</Private>
Expand All @@ -66,6 +70,7 @@
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
<Reference Include="System.Net" />
<Reference Include="System.Net.Http" />
Expand All @@ -79,6 +84,7 @@
<ItemGroup>
<Compile Include="OAuth2\GoogleCredentialTests.cs" />
<Compile Include="OAuth2\DefaultCredentialProviderTests.cs" />
<Compile Include="OAuth2\ServiceAccountCredentialTests.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="..\CommonAssemblyInfo.cs">
Expand All @@ -98,6 +104,10 @@
<Project>{e83c6d98-c348-4944-ad7f-c4e428141079}</Project>
<Name>GoogleApis.Core_Net45</Name>
</ProjectReference>
<ProjectReference Include="..\GoogleApis.Tests\GoogleApis.Tests.csproj">
<Project>{9a8aa9ef-6904-43d8-8a26-0ab62069c2dc}</Project>
<Name>GoogleApis.Tests</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
Expand All @@ -106,6 +116,7 @@
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using Google.Apis.Json;
using Google.Apis.Tests;
using NUnit.Framework;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.IO.Pem;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace Google.Apis.Auth.OAuth2
{
[TestFixture]
public class ServiceAccountCredentialTests
{

[Test]
public async Task ValidLocallySignedAccessToken_FromPrivateKey()
{
const string dummyServiceAccountCredentialFileContents = @"{
""private_key_id"": ""PRIVATE_KEY_ID"",
""private_key"": ""-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJJM6HT4s6btOsfe
2x4zrzrwSUtmtR37XTTi0sPARTDF8uzmXy8UnE5RcVJzEH5T2Ssz/ylX4Sl/CI4L
no1l8j9GiHJb49LSRjWe4Yx936q0Xj9H0R1HTxvjUPqwAsTwy2fKBTog+q1frqc9
o8s2r6LYivUGDVbhuUzCaMJsf+x3AgMBAAECgYEAi0FTXsu/zRswAUGaViQiHjrL
uU65BSHXNVjV/2fLNEKnGWGqpli68z1IXY+S2nwbUak7rnGsq9/0F6jtsW+hZbLk
KXUOuuExpeC5Kd6ngWX/f2jqmhlUabiQijU9cVk7pMq8EHkRtvlosnMTUAEzempu
QUPwn1PZHhmJkBvZ4lECQQDCErrxl+e3BwUDcS0yVEEmCNSG6xdXs2878b8rzbe7
3Mmi6SuuOLi3PU92J+j+f/MOdtYrk13mEDdYmd5dhrt5AkEAwPvDEsDT/W4y4h5n
gv1awGBA5aLFE1JNWM/Gwn4D1cGpEDHKFREaBtxMDCASpHJuw8r7zUywpKhmBZcf
GS37bwJANdSAKfbafLfjuhqwUJ9yGpykZm/a36aTmerp/bpn1iHdg+RtCzwMcDb/
TWSwibbvsflgWmHbz657y4WSWhq+8QJAWrpCNN/ZCk2zuGDo80lfUBAwkoVat8G6
wWU1oZyS+vzIGef+hLb8kHsjeZPej9eIwZ39kcBbT54oELrCkRjwGwJAQ8V2A7lT
ZUp8AsbVqF6rbLiiUfJMo2btGclQu4DEVyS+ymFA65tXDLUuR9EDqJYdqHNZJ5B8
4Z5p2prkjWTLcA==
-----END PRIVATE KEY-----"",
""client_email"": ""CLIENT_EMAIL"",
""client_id"": ""CLIENT_ID"",
""type"": ""service_account""}";

var credentialParameters = NewtonsoftJsonSerializer.Instance.Deserialize<JsonCredentialParameters>(dummyServiceAccountCredentialFileContents);
var initializer = new ServiceAccountCredential.Initializer(credentialParameters.ClientEmail)

This comment was marked as spam.

This comment was marked as spam.

{
Clock = new MockClock { UtcNow = new DateTime(2016, 1, 1, 0, 0, 0, DateTimeKind.Utc) }
};
var cred = new ServiceAccountCredential(initializer.FromPrivateKey(credentialParameters.PrivateKey));

Assert.That(cred.Scopes?.Any(), Is.False); // HasScopes must be false for the type of access token we want to test.

string accessToken = await cred.GetAccessTokenForRequestAsync("http://authurl/");

string expectedToken =
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJDTElFTlRfRU1BSUwiLCJz" +
"dWIiOiJDTElFTlRfRU1BSUwiLCJhdWQiOiJodHRwOi8vYXV0aHVybC8iLCJleHAiOjE0N" +
"TE2MTAwMDAsImlhdCI6MTQ1MTYwNjQwMH0.WLljSaAqxMVZnAxFA2SvpA3n2WRlQW71Nb" +
"CUkbN-ZI-EWoL-HhgiV_3ISrXMvbDHYhBR0vvtXE0PcRcsMEf51Y0jV4DXZ8hf-QJFq7O" +
"Hrepwe93dnDE6uNVnbj41_0phuy1WKwae29Qp2aPI2Y8E8Z2tXQlF87E_MdgjXVeTF8k";
Assert.That(accessToken, Is.EqualTo(expectedToken));
}

#if !NETSTANDARD // ServiceAccountCredential initialization from X509 cert not currently supported on netstandard.
[Test]
public async Task ValidLocallySignedAccessToken_FromX509Certificate()
{
const string sPrivateKey = @"
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALQoMIfUT93VMvb5
4U5dsJLXD1D6z4o/VuVlNUaf5xH01qAv8Egpxe6f5frLB/p1eNyDjNIZ3QitD6aN
9Vfh4ifg6txBuBJtIMfSDvRJITNZ2SjKJREbflZuBk0HINKYQk2H+3TIzUbE/pN4
Cu6Mids5L/oVOnRWIe3bEC+PHR7ZAgMBAAECgYBI1qrwb+2ukeFeK59lcMnQRLVD
l3RLv9ohOy80E7h38RbJgzhR5NnK5ck1AduC7vXjqihIVf6g4F+ghmq4knI94KPQ
2fOyQGVV6jVeRVqMusx7tP9V1H26yABiK6TPklcCsWwADFmexfOxfIgbBGSmlbAd
N2/ad1Xog1xDebrbEQJBAO+2qSIDX1ahfxUyvvcMaix6Mrh/OYKb5aH1Y/7MfAav
lpbQavQHNvak2147aPNxy0ZvWdJ2HVA7hUMkgysYWSUCQQDAZalkZMSrve+Onh55
U+w5xjLklhh8PhYxx8plT8ae9VG75dGvWSz/W81B3ILg2lrNU6o08NKBJdZsJYmc
BeKlAkBtJh3zF9gEaTqlW1rqwKNjpyyLJ5r3JqczzLmAXnmmzbLi7vmULejP+5bL
XH/YQZtOcgtTMmb8jm2Kegijyc1lAkANGt+e5v4+dIGMxVhuCzlb9hQhXdftHo2E
dodivzxYN32Jvu25c+mMu0QP6GVBy53Dvp8pW/36rgkc9LGa3wvBAkEA7dGAl95T
UeNFVfuzkYNtQppcSgrx1oTcpTHoNgcvk8AgBf4yDdJJyo0IUONmgJCVffc1aTWn
nf/8cW9YC+0icQ==";
const string sCert = @"
MIICNDCCAZ2gAwIBAgIJAKl3qU1+NsuuMA0GCSqGSIb3DQEBCwUAMDMxCzAJBgNV
BAYTAkdCMRMwEQYDVQQIDApTb21lLVN0YXRlMQ8wDQYDVQQKDAZHb29nbGUwHhcN
MTYwODEwMTQwOTQ2WhcNNDMxMjI3MTQwOTQ2WjAzMQswCQYDVQQGEwJHQjETMBEG
A1UECAwKU29tZS1TdGF0ZTEPMA0GA1UECgwGR29vZ2xlMIGfMA0GCSqGSIb3DQEB
AQUAA4GNADCBiQKBgQC0KDCH1E/d1TL2+eFOXbCS1w9Q+s+KP1blZTVGn+cR9Nag
L/BIKcXun+X6ywf6dXjcg4zSGd0IrQ+mjfVX4eIn4OrcQbgSbSDH0g70SSEzWdko
yiURG35WbgZNByDSmEJNh/t0yM1GxP6TeArujInbOS/6FTp0ViHt2xAvjx0e2QID
AQABo1AwTjAdBgNVHQ4EFgQUMqzJi099PA8ML5CV1OSiHgiTGoUwHwYDVR0jBBgw
FoAUMqzJi099PA8ML5CV1OSiHgiTGoUwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B
AQsFAAOBgQBQ9cMInb2rEcg8TTYq8MjDEegHWLUI9Dq/IvP/FHyKDczza4eX8m+G
3buutN74pX2/GHRgqvqEvvUUuAQUnZ36k6KjTNxfzNLiSXDPNeYmy6PWsUZy4Rye
/Van/ePiXdipTKMiUyl7V6dTjkE5p/e372wNVXUpcxOMmYdWmzSMvg==";

var privateParams = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(sPrivateKey));
var x509Cert = new X509Certificate2(Convert.FromBase64String(sCert));
var rsaParameters = DotNetUtilities.ToRSAParameters(privateParams);
var privateKey = new System.Security.Cryptography.RSACryptoServiceProvider();
privateKey.ImportParameters(rsaParameters);
x509Cert.PrivateKey = privateKey;

var initializer = new ServiceAccountCredential.Initializer("some-id")
{
Clock = new MockClock { UtcNow = new DateTime(2016, 1, 1, 0, 0, 0, DateTimeKind.Utc) }
};
var cred = new ServiceAccountCredential(initializer.FromCertificate(x509Cert));

Assert.That(cred.Scopes?.Any(), Is.False); // HasScopes must be false for the type of access token we want to test.

string accessToken = await cred.GetAccessTokenForRequestAsync("http://authurl/");

string expectedToken =
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzb21lLWlkIiwic3ViIjoi" +
"c29tZS1pZCIsImF1ZCI6Imh0dHA6Ly9hdXRodXJsLyIsImV4cCI6MTQ1MTYxMDAwMCwia" +
"WF0IjoxNDUxNjA2NDAwfQ.GfpDHgrFi4ZlGC5LuJEarLU4_eTrT5PVa-S40YtkdB2E1f3" +
"4naYG2ItcfBEFg7Gbdkr1cIAyipuhEd2yLfPmWGwhOwVcBRNyK_J5w8RodS44mxNJwau0" +
"jKy4x1K20ybLqcnNgzE0wag6fi5GHwdNIB0URdHDTiC88CRYdl1CIdk";
Assert.That(accessToken, Is.EqualTo(expectedToken));
}
#endif
}
}
4 changes: 3 additions & 1 deletion Src/Support/GoogleApis.Auth.DotNet4.Tests/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ***********************************************************************

#if !NETSTANDARD
using NUnitLite;

namespace NUnitLite.Tests
Expand All @@ -38,4 +39,5 @@ public static int Main(string[] args)
return new AutoRun().Execute(args);
}
}
}
}
#endif
1 change: 1 addition & 0 deletions Src/Support/GoogleApis.Auth.DotNet4.Tests/packages.config
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
<package id="Moq" version="4.2.1402.2112" targetFramework="net45" />
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
<package id="NUnit" version="3.2.1" targetFramework="net45" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
<Compile Include="..\CommonAssemblyInfo.cs">
<Link>Properties\CommonAssemblyInfo.cs</Link>
</Compile>
<Compile Include="OAuth2\RsaStandard.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
Expand All @@ -68,7 +69,6 @@
<Name>GoogleApis.Core_NetStandard</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="..\GoogleApis.Auth.PlatformServices_Shared\GoogleApis.Auth.PlatformServices_Shared.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Expand Down
77 changes: 77 additions & 0 deletions Src/Support/GoogleApis.Auth.NetStandard/OAuth2/RsaStandard.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using System;
using System.IO;
using System.Security.Cryptography;

namespace Google.Apis.Auth.OAuth2
{
/// <summary>
/// An implementation of RSA, just sufficient for the needs of ServiceAccountCredential.
/// This is not a general-purpose implementation of RSA.
/// </summary>
internal class RsaStandard : RSA
{
private readonly RsaPrivateCrtKeyParameters _parameters;

public RsaStandard(RsaPrivateCrtKeyParameters parameters)
{
_parameters = parameters;
}

public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
{
throw new NotImplementedException();
}

public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding)
{
throw new NotImplementedException();
}

public override RSAParameters ExportParameters(bool includePrivateParameters)
{
throw new NotImplementedException();
}

public override void ImportParameters(RSAParameters parameters)
{
throw new NotImplementedException();
}

public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
{
if (hashAlgorithm != HashAlgorithmName.SHA256)
{
throw new ArgumentException(
$"Unsupported HashAlgorithmName '{hashAlgorithm}', only SHA256 supported.", nameof(hashAlgorithm));
}
if (padding != RSASignaturePadding.Pkcs1)
{
throw new ArgumentException(
$"Unsupported RSASignaturePadding '{padding}', only Pkcs1 supported.", nameof(padding));
}
var signer = new RsaDigestSigner(new NullDigest(), NistObjectIdentifiers.IdSha256);
signer.Init(true, _parameters);
signer.BlockUpdate(hash, 0, hash.Length);
return signer.GenerateSignature();
}

public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
{
throw new NotImplementedException();
}

protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
{
throw new NotImplementedException();
}

protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
{
throw new NotImplementedException();
}
}
}
12 changes: 12 additions & 0 deletions Src/Support/GoogleApis.Auth.NetStandard/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,17 @@ limitations under the License.
*/

using System.Reflection;
using System.Runtime.CompilerServices;

[assembly: AssemblyTitle("Google.Apis.Auth.PlatformServices")]

#if SIGNED
[assembly: InternalsVisibleTo("GoogleApis.Auth.Tests_dotnetcore,PublicKey=" +
"00240000048000009400000006020000002400005253413100040000010001003d69fa08add2ea" +
"7341cc102edb2f3a59bb49e7f7c8bf1bd96d494013c194f4d80ee30278f20e08a0b7cb863d6522" +
"d8c1c0071dd36748297deefeb99e899e6a80b9ddc490e88ea566d2f7d4f442211f7beb6b2387fb" +
"435bfaa3ecfe7afc0184cc46f80a5866e6bb8eb73f64a3964ed82f6a5036b91b1ac93e1f44508b" +
"65e51fce")]
#else
[assembly: InternalsVisibleTo("GoogleApis.Auth.Tests_dotnetcore")]
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,9 @@ private static GoogleCredential CreateDefaultCredentialFromJson(JsonCredentialPa
{
case JsonCredentialParameters.AuthorizedUserCredentialType:
return new GoogleCredential(CreateUserCredentialFromJson(credentialParameters));
#if !NETSTANDARD
case JsonCredentialParameters.ServiceAccountCredentialType:
return GoogleCredential.FromCredential(
CreateServiceAccountCredentialFromJson(credentialParameters));
#else
case JsonCredentialParameters.ServiceAccountCredentialType:
throw new InvalidOperationException("Service Account credentials are not supported in .NET Core.");
#endif
default:
throw new InvalidOperationException(
String.Format("Error creating credential from JSON. Unrecognized credential type {0}.",
Expand Down Expand Up @@ -224,7 +219,6 @@ private static UserCredential CreateUserCredentialFromJson(JsonCredentialParamet
return new UserCredential(flow, "ApplicationDefaultCredentials", token);
}

#if !NETSTANDARD
/// <summary>Creates a <see cref="ServiceAccountCredential"/> from JSON data.</summary>
private static ServiceAccountCredential CreateServiceAccountCredentialFromJson(
JsonCredentialParameters credentialParameters)
Expand All @@ -238,7 +232,6 @@ private static ServiceAccountCredential CreateServiceAccountCredentialFromJson(
var initializer = new ServiceAccountCredential.Initializer(credentialParameters.ClientEmail);
return new ServiceAccountCredential(initializer.FromPrivateKey(credentialParameters.PrivateKey));
}
#endif

/// <summary>
/// Returns platform-specific well known credential file path. This file is created by
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,38 +148,25 @@ public GoogleCredential CreateScoped(params string[] scopes)
return CreateScoped((IEnumerable<string>) scopes);
}

#region IConfigurableHttpClientInitializer

void IConfigurableHttpClientInitializer.Initialize(ConfigurableHttpClient httpClient)

This comment was marked as spam.

This comment was marked as spam.

{
credential.Initialize(httpClient);
}

#endregion

#region ITokenAccess

Task<string> ITokenAccess.GetAccessTokenForRequestAsync(string authUri, CancellationToken cancellationToken)
{
return credential.GetAccessTokenForRequestAsync(authUri, cancellationToken);
}

#endregion

/// <summary>Provides access to the underlying credential object</summary>
internal ICredential UnderlyingCredential { get { return credential; } }

#if !NETSTANDARD
#region Factory methods

/// <summary>Creates a <c>GoogleCredential</c> wrapping a <see cref="ServiceAccountCredential"/>.</summary>
internal static GoogleCredential FromCredential(ServiceAccountCredential credential)
{
return new ServiceAccountGoogleCredential(credential);
}

#endregion

/// <summary>
/// Wraps <c>ServiceAccountCredential</c> as <c>GoogleCredential</c>.
/// We need this subclass because wrapping <c>ServiceAccountCredential</c> (unlike other wrapped credential
Expand All @@ -190,8 +177,6 @@ internal class ServiceAccountGoogleCredential : GoogleCredential
public ServiceAccountGoogleCredential(ServiceAccountCredential credential)
: base(credential) { }

#region GoogleCredential overrides

public override bool IsCreateScopedRequired
{
get { return !(credential as ServiceAccountCredential).HasScopes; }
Expand All @@ -208,9 +193,6 @@ public override GoogleCredential CreateScoped(IEnumerable<string> scopes)
};
return new ServiceAccountGoogleCredential(new ServiceAccountCredential(initializer));
}

#endregion
}
#endif
}
}
Loading