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

[WIP] "Sign in With Apple" provider #318

Draft
wants to merge 19 commits into
base: dev
from
Draft
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

Fix Linux and macOS secret generation

Work around platform differences between Windows and Linux/macOS by supporting .p12/.pfx certificates for Linux/macOS and using p8 for Windows.
.NET Core 3.0 adds support for .p8 on both platforms.
  • Loading branch information...
martincostello committed Jun 9, 2019
commit 0683f718719bf7f225e954c9eec57f7c909b7361
@@ -70,6 +70,10 @@ public new AppleAuthenticationEvents Events
/// Gets or sets an optional delegate to get the raw bytes of the client's private key
/// which is passed the value of the <see cref="KeyId"/> property.
/// </summary>
/// <remarks>
/// On Windows, the private key should be in PKCS #8 (<c>.p8</c>) format.
/// On Linux and macOS, the private key should be PKCS #12 (<c>.pfx</c>) format.
/// </remarks>
public Func<string, Task<byte[]>> PrivateKeyBytes { get; set; }

/// <summary>
@@ -6,8 +6,10 @@

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Runtime.InteropServices;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
@@ -79,17 +81,9 @@ public override async Task<string> GenerateAsync([NotNull] AppleGenerateClientSe
byte[] keyBlob = await _keyStore.LoadPrivateKeyAsync(context);
string clientSecret;

using (var privateKey = CngKey.Import(keyBlob, CngKeyBlobFormat.Pkcs8PrivateBlob))
using (var algorithm = new ECDsaCng(privateKey))
using (var algorithm = CreateAlgorithm(keyBlob))
{
algorithm.HashAlgorithm = CngAlgorithm.Sha256;

var key = new ECDsaSecurityKey(algorithm)
{
KeyId = context.Options.KeyId,
};

tokenDescriptor.SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.EcdsaSha256Signature);
tokenDescriptor.SigningCredentials = CreateSigningCredentials(context.Options.KeyId, algorithm);

clientSecret = _tokenHandler.CreateEncodedJwt(tokenDescriptor);
}
@@ -98,5 +92,37 @@ public override async Task<string> GenerateAsync([NotNull] AppleGenerateClientSe

return (clientSecret, expiresAt);
}

private ECDsa CreateAlgorithm(byte[] keyBlob)
{
// This becomes xplat in .NET Core 3.0: https://github.com/dotnet/corefx/pull/30271
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
CreateAlgorithmWindows(keyBlob) :
CreateAlgorithmLinuxOrMac(keyBlob);
}

private ECDsa CreateAlgorithmLinuxOrMac(byte[] keyBlob)
{
// Does not support .p8 files in .NET Core 2.x as-per https://github.com/dotnet/corefx/issues/18733#issuecomment-296723615
using (var cert = new X509Certificate2(keyBlob, string.Empty))
{
return cert.GetECDsaPrivateKey();
}
}

private ECDsa CreateAlgorithmWindows(byte[] keyBlob)
{
// Only Windows supports .p8 files in .NET Core 2.0 as-per https://github.com/dotnet/corefx/issues/18733
using (var privateKey = CngKey.Import(keyBlob, CngKeyBlobFormat.Pkcs8PrivateBlob))
{
return new ECDsaCng(privateKey) { HashAlgorithm = CngAlgorithm.Sha256 };
}
}

private SigningCredentials CreateSigningCredentials(string keyId, ECDsa algorithm)
{
var key = new ECDsaSecurityKey(algorithm) { KeyId = keyId };
return new SigningCredentials(key, SecurityAlgorithms.EcdsaSha256Signature);
}
}
}
@@ -7,6 +7,7 @@
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
@@ -20,7 +21,10 @@ namespace AspNet.Security.OAuth.Apple
{
public static class AppleClientSecretGeneratorTests
{
private static readonly byte[] TestPrivateKey = Convert.FromBase64String("MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgU208KCg/doqiSzsVF5sknVtYSgt8/3oiYGbvryIRrzSgCgYIKoZIzj0DAQehRANCAAQfrvDWizEnWAzB2Hx2r/NyvIBO6KGBDL7wkZoKnz4Sm4+1P1dhD9fVEhbsdoq9RKEf8dvzTOZMaC/iLqZFKSN6");
internal static readonly byte[] TestPrivateKey =
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
Convert.FromBase64String("MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgU208KCg/doqiSzsVF5sknVtYSgt8/3oiYGbvryIRrzSgCgYIKoZIzj0DAQehRANCAAQfrvDWizEnWAzB2Hx2r/NyvIBO6KGBDL7wkZoKnz4Sm4+1P1dhD9fVEhbsdoq9RKEf8dvzTOZMaC/iLqZFKSN6") :
Convert.FromBase64String("MIIEagIBAzCCBDAGCSqGSIb3DQEHAaCCBCEEggQdMIIEGTCCAw8GCSqGSIb3DQEHBqCCAwAwggL8AgEAMIIC9QYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIim9BBUiXkDgCAggAgIICyGrISvsUV1o7fVvskmnfWPQqScCIDx/P93fqnes0EwtQTMIaCFIR1i9TH/6NlrusAD9qHXE7W3i/3gNsiuMGC4cYim4ym8GBhyPxWJqcubSpOVv5adja5gmz7G1v6iB6H9mg2TNLR2RQx4dddjA4Q4vSXYp2DOrvGIB8Fw9Cjx5BrQYEL78IMzB3DmNtLoFs/ny7a0WjoxnyCeT8272rzOgiuPN4Fhtj+pJYorIeoEcyJ3DAvZur6CotHANYVhSETI8tUejX5oqpg2LuY5yzxRrgy81PbabWA92TvvNMy/3/WuFSO3RyhAj4+87+ru8q6cgNT3IcZ16YXMG+XQX9eyp1EwxeDe5he3/4FEfbQH0tMD7E3MAQUjUXI/wzhCCI5HvpLXtf0N4tc8b178ykc+1oZ3zpYs4mOEHjv/xcM27C6L5YeZD36MX0LHjUQGe7ezNSYewqMR8LRwqzZ6QpLY5Dv+izx297hcKuh2bvA7MatJuI+VlK0g7gTrKYp6OHaQH6Us8F7BlO+jI8AzippV8wsJKo1VwgMVPMku4ufKAdxTuSoJP55azbze5nEebzhOidazVMgttyPrKB8QzCa19iHEzqqjWEEvDerZt5K5Em7CVxnnNVwaS/Cm70/oe4W2hpS7D4Rrj7Q4pOK6paJhxa/RZGUWtaddCSVyvI8Er4aGI0xJy1rOXBEs2yBl9z30PxsPl2ddvCV2ax0E5semTDKkFhS35OHsiFAjLUyDc0tE/Erh2+u16rzk0ceKRpmK0kanCQtqS5Amd6zaPk/D+fOIZY5tHUATHX5OGfCpT4vfTFSnJX66IAeLBOKO67sveiXrFfZanxnHAEWc6a5xHCBPZvSKlGdCsm86rZR/tXESGLWEbO3T7X7L694Rtq37yg2HjW8SQ6Y5hzYWjjLjU2F5kBJj5q9UVGIuBRUucnT+8YPJ95/00wggECBgkqhkiG9w0BBwGggfQEgfEwge4wgesGCyqGSIb3DQEMCgECoIG0MIGxMBwGCiqGSIb3DQEMAQMwDgQICc6+CvVyhsMCAggABIGQWzkumOp4ivI9Y8uECZwnmhXGn2YoZyfBjH6LC1lBtjQC6qs5PcoeqmL0Ig/6ZyPGKZ56kZXLJfWv7hnLAGcBAgHhMrLl73XNxtiIdA/FVWZvNGrzETM9U5JpfhFOZvWgoAZDeGnOirrHjBOtihaCMBscel5ZmULjJXiIr5kiVfByYX+RBrSIjaAwWRQWfK6hMSUwIwYJKoZIhvcNAQkVMRYEFF7DK7P8nOIRMzRro6ajG9hRWl04MDEwITAJBgUrDgMCGgUABBTqJGte1FPTsA+57DXU5WGnb+NfiQQIZ5eHWqzCYQwCAggA");

[Fact]
public static async Task GenerateAsync_Generates_Valid_Signed_Jwt()
@@ -81,8 +81,7 @@ void ConfigureServices(IServiceCollection services)
options.PrivateKeyBytes = (keyId) =>
{
Assert.Equal("my-key-id", keyId);
string privateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgU208KCg/doqiSzsVF5sknVtYSgt8/3oiYGbvryIRrzSgCgYIKoZIzj0DAQehRANCAAQfrvDWizEnWAzB2Hx2r/NyvIBO6KGBDL7wkZoKnz4Sm4+1P1dhD9fVEhbsdoq9RKEf8dvzTOZMaC/iLqZFKSN6";
return Task.FromResult(Convert.FromBase64String(privateKey));
return Task.FromResult(AppleClientSecretGeneratorTests.TestPrivateKey);
};
});
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.