diff --git a/PnP-IdentityModel.sln b/PnP-IdentityModel.sln index 79f666d..e6001f3 100644 --- a/PnP-IdentityModel.sln +++ b/PnP-IdentityModel.sln @@ -1,10 +1,12 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +VisualStudioVersion = 15.0.27004.2002 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharePointPnP.IdentityModel.Extensions", "SharePointPnP.IdentityModel.Extensions\SharePointPnP.IdentityModel.Extensions.csproj", "{EB239573-1869-4DF4-8973-E01B7796DA02}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharePointPnP.IdentityModel.Extensions.Test", "SharePointPnP.IdentityModel.Extensions.Test\SharePointPnP.IdentityModel.Extensions.Test.csproj", "{575BB00A-ED1B-4B68-9F1A-C7D3FAEE0806}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,8 +17,15 @@ Global {EB239573-1869-4DF4-8973-E01B7796DA02}.Debug|Any CPU.Build.0 = Debug|Any CPU {EB239573-1869-4DF4-8973-E01B7796DA02}.Release|Any CPU.ActiveCfg = Release|Any CPU {EB239573-1869-4DF4-8973-E01B7796DA02}.Release|Any CPU.Build.0 = Release|Any CPU + {575BB00A-ED1B-4B68-9F1A-C7D3FAEE0806}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {575BB00A-ED1B-4B68-9F1A-C7D3FAEE0806}.Debug|Any CPU.Build.0 = Debug|Any CPU + {575BB00A-ED1B-4B68-9F1A-C7D3FAEE0806}.Release|Any CPU.ActiveCfg = Release|Any CPU + {575BB00A-ED1B-4B68-9F1A-C7D3FAEE0806}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9F2D97AD-51E6-4004-A7D2-E6EF0E6E86B0} + EndGlobalSection EndGlobal diff --git a/README.md b/README.md index ec3ab85..68a49f3 100644 --- a/README.md +++ b/README.md @@ -2,5 +2,6 @@ This repository holds code related to authentication in SharePoint. -##SharePointPnP.IdentityModel.Extensions -Originally, Microsoft.IdentityModel.Extensions.dll is where the code for SharePoint provider-hosted apps OAuth and S2S token processing is located. Microsoft.IdentityModel.Extensions is not maintained by anyone, but SharePoint add-ins, SharePointPnP.Core and a few other things depend on it. SharePointPnP.IdentityModel.Extensions is a port of that library created by the PnP team. We reference it in OfficeDevPnP.Core (and all other supporting solutions) instead of depending on Microsoft.IdentityModel.Extensions. +## SharePointPnP.IdentityModel.Extensions + +Originally, Microsoft.IdentityModel.Extensions.dll is where the code for SharePoint provider-hosted apps OAuth and S2S token processing is located. `Microsoft.IdentityModel.Extensions` is not maintained by anyone, but SharePoint add-ins, SharePointPnP.Core and a few other things depend on it. `SharePointPnP.IdentityModel.Extensions` is a port of that library created by the PnP team. We reference it in [OfficeDevPnP.Core](https://github.com/SharePoint/PnP-Sites-Core/tree/master/Core/OfficeDevPnP.Core) (and all other supporting solutions) instead of depending on `Microsoft.IdentityModel.Extensions`. diff --git a/SHarePointPnP.IdentityModel.Extensions.Test/Certificate/Test.pfx b/SHarePointPnP.IdentityModel.Extensions.Test/Certificate/Test.pfx new file mode 100644 index 0000000..d8872d1 Binary files /dev/null and b/SHarePointPnP.IdentityModel.Extensions.Test/Certificate/Test.pfx differ diff --git a/SharePointPnP.IdentityModel.Extensions.Test/Certificate/Test Base64.cer b/SharePointPnP.IdentityModel.Extensions.Test/Certificate/Test Base64.cer new file mode 100644 index 0000000..5e8e934 --- /dev/null +++ b/SharePointPnP.IdentityModel.Extensions.Test/Certificate/Test Base64.cer @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC5DCCAdCgAwIBAgIQyGg2fpyqeZVFAMQL+pssrDAJBgUrDgMCHQUAMA8xDTAL +BgNVBAMTBFRlc3QwHhcNMTYxMjMxMjMwMDAwWhcNMTcxMjMxMjMwMDAwWjAPMQ0w +CwYDVQQDEwRUZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAycVE +3zBx1GX2TSGSFch9vwsfk3mPjkJztCGoyO/6taZxq2SiHo6SxzZhP46PQ4NeBV8A +kzIDlmAzbGETXYA5Ntign23m/wtCGVb521d+b+fAoiykrvLPp4+CvExjB8cDUIKS +na0w3WsdhPX2RzAQ8UU7bxPq1skExrdxj6+pAXBU0qp+l3aRwJlMJ3j7apWMV/a3 +Tr4V8203QgtRoAnl4SEY8S+mtEGGfmCfX8WCLbLJdN/UssOIe/ZOGd1iKlL7h+BG +h/6V5N+rDwxMzCUdignCt8VAUv3jz47EAfgI9DrHnmDO7a0iJC4rHrNmU6Cs8UXM +WO2JMUcqn9PjOjWPpQIDAQABo0QwQjBABgNVHQEEOTA3gBB025fpt4u1ul5U+kmP +t0unoREwDzENMAsGA1UEAxMEVGVzdIIQyGg2fpyqeZVFAMQL+pssrDAJBgUrDgMC +HQUAA4IBAQBjaZQ7r83mLOsAOrCa1etjJXQ+VP6LIlExxEfQDpQdYMB/mElRfgjx +NSTmCMpmrVvSKoePZoqV0d0LHUvZP4s8UzEUGVUuO8XHXuRSh/1A0PlAVg0v5jMw +dxg4MBGYnYBHCGdYvYJFvfo3sgS7pgzhbj7yX1PPmpHBY2aXn3B24ZIi0tOUY4vX +Ve+J8uDe22D3DvpJI3l7BSQoTzwC5M/Rd1I4k9Vw649FFv6cXJWFbX70bDMkVq1Z +xkmXR1myjYzHnhDR4uOMmjCkyiSUycrMKTbQpKV1mSjPkGDllVYUbOFdH/dFDU68 +mtH5KIdA0/npPZnzG0XTzH1YMIbG4JDD +-----END CERTIFICATE----- diff --git a/SharePointPnP.IdentityModel.Extensions.Test/Certificate/Test.cer b/SharePointPnP.IdentityModel.Extensions.Test/Certificate/Test.cer new file mode 100644 index 0000000..2f80b31 Binary files /dev/null and b/SharePointPnP.IdentityModel.Extensions.Test/Certificate/Test.cer differ diff --git a/SharePointPnP.IdentityModel.Extensions.Test/Certificate/Test.pvk b/SharePointPnP.IdentityModel.Extensions.Test/Certificate/Test.pvk new file mode 100644 index 0000000..0acad2c Binary files /dev/null and b/SharePointPnP.IdentityModel.Extensions.Test/Certificate/Test.pvk differ diff --git a/SharePointPnP.IdentityModel.Extensions.Test/JwtTokenCreationTest.cs b/SharePointPnP.IdentityModel.Extensions.Test/JwtTokenCreationTest.cs new file mode 100644 index 0000000..86a1708 --- /dev/null +++ b/SharePointPnP.IdentityModel.Extensions.Test/JwtTokenCreationTest.cs @@ -0,0 +1,71 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SharePointPnP.IdentityModel.Extensions.S2S.Tokens; +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens; +using System.Security.Cryptography.X509Certificates; +using X509SigningCredentials = Microsoft.IdentityModel.SecurityTokenService.X509SigningCredentials; + +namespace SharePointPnP.IdentityModel.Extensions.Test +{ + [TestClass] + public class JwtTokenCreationTest + { + [TestMethod] + public void CreateTokenTest() + { + var certFile = @".\Certificate\Test.pfx"; + var password = "Password1+"; + + var certificate = new X509Certificate2(certFile, password); + var signingCredentials = new X509SigningCredentials(certificate, SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest); + + var actorClaims = new List(); + actorClaims.Add(new JsonWebTokenClaim(JsonWebTokenConstants.ReservedClaims.NameIdentifier, "TestName")); + var actorToken = new JsonWebSecurityToken( + issuer: "Test Issuer", + audience: "TestAudience", + validFrom: new DateTime(2017, 1, 1), + validTo: new DateTime(2017, 12, 31, 23, 59, 59), + signingCredentials: signingCredentials, + claims: actorClaims); + + var actorTokenString = new JsonWebSecurityTokenHandler().WriteTokenAsString(actorToken); + + Assert.IsNotNull(actorTokenString); + Assert.AreEqual("eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjhMcjBkRjJGRC00QXlUbXhzNnh4enRqYUl6WSJ9.eyJhdWQiOiJUZXN0QXVkaWVuY2UiLCJpc3MiOiJUZXN0IElzc3VlciIsIm5iZiI6IjE0ODMyMjUyMDAiLCJleHAiOiIxNTE0NzYxMTk5IiwibmFtZWlkIjoiVGVzdE5hbWUifQ.Z_8DKOQiXldAMyJj2BGNzfJd2cTm_XqEcgsAOFyeKwHGJ9yx4uYUM9V7FAUFRPzW7fsb1I2LIS8RDo_riw9m5c8xeequ1noAYbydOZIDHuM9tefplCsve0_cIzek6lV0B0jykDj7OFtJMsDs9TQEtKcjVDGkBK4BnHUwUTLB_lYdzTjMm7WXOpFxz5c74tP5vaER1nuFhtftO5Hsy7jKyRRgdhKJ2o3Do_-LGdEfG9m51dUSb5E8odVGu1vGBQVsc88a11y5uFzORL7cm6hu2RwEELIzfd7bdHruQ9BB5mpS4AGzD4QxtHs2jgYXQ4-HqWFvnpJ-Z89_xWcmo-wOKA", actorTokenString); + } + + [TestMethod] + public void CreateTwoTokensTest() + { + var certFile = @".\Certificate\Test.pfx"; + var password = "Password1+"; + + var certificate = new X509Certificate2(certFile, password); + var signingCredentials = new X509SigningCredentials(certificate, SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest); + + var actorClaims = new List(); + actorClaims.Add(new JsonWebTokenClaim(JsonWebTokenConstants.ReservedClaims.NameIdentifier, "TestName")); + var actorToken = new JsonWebSecurityToken( + issuer: "Test Issuer", + audience: "TestAudience", + validFrom: new DateTime(2017, 1, 1), + validTo: new DateTime(2017, 12, 31, 23, 59, 59), + signingCredentials: signingCredentials, + claims: actorClaims); + + var tokenHandler = new JsonWebSecurityTokenHandler(); + var firstActorTokenString = tokenHandler.WriteTokenAsString(actorToken); + + Assert.IsNotNull(firstActorTokenString); + Assert.AreEqual("eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjhMcjBkRjJGRC00QXlUbXhzNnh4enRqYUl6WSJ9.eyJhdWQiOiJUZXN0QXVkaWVuY2UiLCJpc3MiOiJUZXN0IElzc3VlciIsIm5iZiI6IjE0ODMyMjUyMDAiLCJleHAiOiIxNTE0NzYxMTk5IiwibmFtZWlkIjoiVGVzdE5hbWUifQ.Z_8DKOQiXldAMyJj2BGNzfJd2cTm_XqEcgsAOFyeKwHGJ9yx4uYUM9V7FAUFRPzW7fsb1I2LIS8RDo_riw9m5c8xeequ1noAYbydOZIDHuM9tefplCsve0_cIzek6lV0B0jykDj7OFtJMsDs9TQEtKcjVDGkBK4BnHUwUTLB_lYdzTjMm7WXOpFxz5c74tP5vaER1nuFhtftO5Hsy7jKyRRgdhKJ2o3Do_-LGdEfG9m51dUSb5E8odVGu1vGBQVsc88a11y5uFzORL7cm6hu2RwEELIzfd7bdHruQ9BB5mpS4AGzD4QxtHs2jgYXQ4-HqWFvnpJ-Z89_xWcmo-wOKA", firstActorTokenString); + + var secondActorTokenString = tokenHandler.WriteTokenAsString(actorToken); + + Assert.IsNotNull(secondActorTokenString); + Assert.AreEqual("eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjhMcjBkRjJGRC00QXlUbXhzNnh4enRqYUl6WSJ9.eyJhdWQiOiJUZXN0QXVkaWVuY2UiLCJpc3MiOiJUZXN0IElzc3VlciIsIm5iZiI6IjE0ODMyMjUyMDAiLCJleHAiOiIxNTE0NzYxMTk5IiwibmFtZWlkIjoiVGVzdE5hbWUifQ.Z_8DKOQiXldAMyJj2BGNzfJd2cTm_XqEcgsAOFyeKwHGJ9yx4uYUM9V7FAUFRPzW7fsb1I2LIS8RDo_riw9m5c8xeequ1noAYbydOZIDHuM9tefplCsve0_cIzek6lV0B0jykDj7OFtJMsDs9TQEtKcjVDGkBK4BnHUwUTLB_lYdzTjMm7WXOpFxz5c74tP5vaER1nuFhtftO5Hsy7jKyRRgdhKJ2o3Do_-LGdEfG9m51dUSb5E8odVGu1vGBQVsc88a11y5uFzORL7cm6hu2RwEELIzfd7bdHruQ9BB5mpS4AGzD4QxtHs2jgYXQ4-HqWFvnpJ-Z89_xWcmo-wOKA", secondActorTokenString); + + } + } +} diff --git a/SharePointPnP.IdentityModel.Extensions.Test/Properties/AssemblyInfo.cs b/SharePointPnP.IdentityModel.Extensions.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..60acf50 --- /dev/null +++ b/SharePointPnP.IdentityModel.Extensions.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("SharePointPnP.IdentityModel.Extensions.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SharePointPnP.IdentityModel.Extensions.Test")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("575bb00a-ed1b-4b68-9f1a-c7d3faee0806")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SharePointPnP.IdentityModel.Extensions.Test/SharePointPnP.IdentityModel.Extensions.Test.csproj b/SharePointPnP.IdentityModel.Extensions.Test/SharePointPnP.IdentityModel.Extensions.Test.csproj new file mode 100644 index 0000000..bcb35fe --- /dev/null +++ b/SharePointPnP.IdentityModel.Extensions.Test/SharePointPnP.IdentityModel.Extensions.Test.csproj @@ -0,0 +1,78 @@ + + + + + Debug + AnyCPU + {575BB00A-ED1B-4B68-9F1A-C7D3FAEE0806} + Library + Properties + SharePointPnP.IdentityModel.Extensions.Test + SharePointPnP.IdentityModel.Extensions.Test + v4.7 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + ..\packages\MSTest.TestFramework.1.1.18\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + + + ..\packages\MSTest.TestFramework.1.1.18\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + + + + + + + + + + + Always + + + + + + {EB239573-1869-4DF4-8973-E01B7796DA02} + SharePointPnP.IdentityModel.Extensions + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/SharePointPnP.IdentityModel.Extensions.Test/packages.config b/SharePointPnP.IdentityModel.Extensions.Test/packages.config new file mode 100644 index 0000000..a4b6ae9 --- /dev/null +++ b/SharePointPnP.IdentityModel.Extensions.Test/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/AsymmetricSignatureProviderFactory.cs b/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/AsymmetricSignatureProviderFactory.cs new file mode 100644 index 0000000..f007bb9 --- /dev/null +++ b/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/AsymmetricSignatureProviderFactory.cs @@ -0,0 +1,41 @@ +using System.IdentityModel.Tokens; +using System.Security.Cryptography; + +namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens +{ + /// + /// Factory that builds the necessary based on the Cryptographic Service Provider we are getting from the + /// framework. Until .NET 4.6 the default Crypt Service Provider is . Starting with .NET 4.7, we are getting RSACng. + /// + internal class AsymmetricSignatureProviderFactory + { + /// + /// Creates a signature provider for an asymmetric encryption scheme. The + /// + /// The asymmetric security key. + /// A that uses the crypto service provider associated with the + /// + /// Until .NET 4.6 the default Crypt Service Provider is . Starting with .NET 4.7, we are getting RSACng. + /// + public static SignatureProvider CreateSignatureProvider(X509AsymmetricSecurityKey asymmetricSecurityKey) + { + Utility.VerifyNonNullArgument("asymmetricSecurityKey", asymmetricSecurityKey); +#if NET46 + var asymmetricAlgorithm = asymmetricSecurityKey.GetAsymmetricAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", true); + if (asymmetricAlgorithm is RSACryptoServiceProvider) + { + return new X509AsymmetricSignatureProvider(asymmetricSecurityKey); + } + if (asymmetricAlgorithm is RSACng) + { + return new X509RsaCngAsymmetricSignatureProvider(asymmetricSecurityKey); + } + + throw new System.InvalidOperationException(string.Format("Could not get asymmetric signature provider of type \"{0}\"", asymmetricAlgorithm.GetType().Name)); +#else + //Older versions of the .NET Framework only know the RSACryptoServiceProvider. In this case, we can use the default implementation + return new X509AsymmetricSignatureProvider(asymmetricSecurityKey); +#endif + } + } +} \ No newline at end of file diff --git a/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/RSACngProxy.cs b/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/RSACngProxy.cs new file mode 100644 index 0000000..c1977ed --- /dev/null +++ b/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/RSACngProxy.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens +{ + internal sealed class RSACngProxy : System.IDisposable + { + private bool _disposed; + + private bool _disposeRsa; + + private System.Security.Cryptography.RSACng _rsa; + + public RSACngProxy(System.Security.Cryptography.RSACng rsa) + { + Utility.VerifyNonNullArgument("rsa", rsa); + + this._rsa = rsa; + } + + private void Dispose(bool disposing) + { + if (!this._disposed) + { + if (disposing && this._disposeRsa && this._rsa != null) + { + this._rsa.Dispose(); + this._rsa = null; + } + this._disposed = true; + } + } + + public byte[] SignData(byte[] signingInput, HashAlgorithmName hashAlgorithm) + { + var signaturePadding = RSASignaturePadding.Pkcs1; + return this._rsa.SignData(signingInput, hashAlgorithm, signaturePadding); + + } + + public bool VerifyData(byte[] signingInput, HashAlgorithmName hashAlgorithm, byte[] signature) + { + return this._rsa.VerifyData(signingInput, signature, hashAlgorithm, RSASignaturePadding.Pkcs1); + + } + + public void Dispose() + { + this.Dispose(true); + System.GC.SuppressFinalize(this); + } + } +} diff --git a/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/SignatureProvider.cs b/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/SignatureProvider.cs index 3f75870..1c4f883 100644 --- a/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/SignatureProvider.cs +++ b/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/SignatureProvider.cs @@ -12,7 +12,7 @@ public static SignatureProvider Create(System.IdentityModel.Tokens.SigningCreden System.IdentityModel.Tokens.X509AsymmetricSecurityKey x509AsymmetricSecurityKey = signingCredentials.SigningKey as System.IdentityModel.Tokens.X509AsymmetricSecurityKey; if (x509AsymmetricSecurityKey != null) { - return new X509AsymmetricSignatureProvider(x509AsymmetricSecurityKey); + return AsymmetricSignatureProviderFactory.CreateSignatureProvider(x509AsymmetricSecurityKey); } System.IdentityModel.Tokens.SymmetricSecurityKey symmetricSecurityKey = signingCredentials.SigningKey as System.IdentityModel.Tokens.SymmetricSecurityKey; if (symmetricSecurityKey != null) diff --git a/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/X509AsymmetricSignatureProvider.cs b/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/X509AsymmetricSignatureProvider.cs index 66faccd..d014383 100644 --- a/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/X509AsymmetricSignatureProvider.cs +++ b/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/X509AsymmetricSignatureProvider.cs @@ -16,7 +16,7 @@ public X509AsymmetricSignatureProvider(System.IdentityModel.Tokens.X509Asymmetri System.Security.Cryptography.RSACryptoServiceProvider rSACryptoServiceProvider = x509Key.GetAsymmetricAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", true) as System.Security.Cryptography.RSACryptoServiceProvider; if (rSACryptoServiceProvider == null) { - throw new System.InvalidOperationException("Could not get algorithm from X509AsymmetricSecurityKey"); + throw new System.InvalidOperationException("Could not get algorithm from X509AsymmetricSecurityKey for \"RSACryptoServiceProvider\""); } this.Initialize(rSACryptoServiceProvider); } diff --git a/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/X509RsaCngAsymmetricSignatureProvider.cs b/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/X509RsaCngAsymmetricSignatureProvider.cs new file mode 100644 index 0000000..2a5f02d --- /dev/null +++ b/SharePointPnP.IdentityModel.Extensions/S2S/Tokens/X509RsaCngAsymmetricSignatureProvider.cs @@ -0,0 +1,94 @@ +#if NET46 +using System; +using System.Security.Cryptography; + +namespace SharePointPnP.IdentityModel.Extensions.S2S.Tokens +{ + internal class X509RsaCngAsymmetricSignatureProvider : SignatureProvider + { + private bool _disposed; + + private RSACngProxy _rsaProxy; + + private HashAlgorithmName? _hashAlgorithm; + + public X509RsaCngAsymmetricSignatureProvider(System.IdentityModel.Tokens.X509AsymmetricSecurityKey x509Key) + { + Utility.VerifyNonNullArgument("x509Key", x509Key); + var rsaCng = x509Key.GetAsymmetricAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", true) as RSACng; + if (rsaCng == null) + { + throw new System.InvalidOperationException("Could not get algorithm from X509AsymmetricSecurityKey for \"RSACng\""); + } + this.Initialize(rsaCng); + } + + public X509RsaCngAsymmetricSignatureProvider(RSACng rsa) + { + this.Initialize(rsa); + } + + protected override void Dispose(bool disposing) + { + if (!this._disposed) + { + if (disposing) + { + if (this._hashAlgorithm != null) + { + this._hashAlgorithm = null; + } + if (this._rsaProxy != null) + { + this._rsaProxy.Dispose(); + this._rsaProxy = null; + } + } + this._disposed = true; + } + } + + private void Initialize(RSACng rsa) + { + if (Utility.RequiresFipsCompliance) + { + CryptoConfig.AddOID("2.16.840.1.101.3.4.2.1", new string[] + { + "SHA256CSP" + }); + CryptoConfig.AddAlgorithm(typeof(SHA256CryptoServiceProvider), new string[] + { + "SHA256CSP" + }); + this._hashAlgorithm = new HashAlgorithmName("SHA256CSP"); + } + else + { + this._hashAlgorithm = new HashAlgorithmName("SHA256"); + } + this._rsaProxy = new RSACngProxy(rsa); + } + + public override byte[] Sign(byte[] signingInput) + { + Utility.VerifyNonNullArgument("signingInput", signingInput); + if (!this._hashAlgorithm.HasValue) + { + throw new NullReferenceException("Hash algorithm has not been set"); + } + return this._rsaProxy.SignData(signingInput, this._hashAlgorithm.Value); + } + + public override bool Verify(byte[] signingInput, byte[] signature) + { + Utility.VerifyNonNullArgument("signingInput", signingInput); + Utility.VerifyNonNullArgument("signature", signature); + if (!this._hashAlgorithm.HasValue) + { + throw new NullReferenceException("Hash algorithm has not been set"); + } + return this._rsaProxy.VerifyData(signingInput, this._hashAlgorithm.Value, signature); + } + } +} +#endif \ No newline at end of file diff --git a/SharePointPnP.IdentityModel.Extensions/SharePointPnP.IdentityModel.Extensions.csproj b/SharePointPnP.IdentityModel.Extensions/SharePointPnP.IdentityModel.Extensions.csproj index 1e31b8e..98a89fa 100644 --- a/SharePointPnP.IdentityModel.Extensions/SharePointPnP.IdentityModel.Extensions.csproj +++ b/SharePointPnP.IdentityModel.Extensions/SharePointPnP.IdentityModel.Extensions.csproj @@ -4,7 +4,7 @@ SharePointPnP.IdentityModel.Extensions 1.2.1 SharePoint Patterns and Practices - net45 + net46 SharePointPnP.IdentityModel.Extensions SharePointPnP.IdentityModel.Extensions false @@ -15,6 +15,10 @@ 1.2.4.0 + + TRACE;DEBUG;NET46 + + @@ -29,4 +33,14 @@ + + + + + + + + + +