Skip to content

Commit a63daf9

Browse files
Merge pull request restsharp#1097 from timotei/develop
Handle RsaSha1 properly in .NET Core 2.0
2 parents 641f428 + ff9b8ad commit a63daf9

File tree

3 files changed

+143
-2
lines changed

3 files changed

+143
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using System;
2+
using System.Security.Cryptography;
3+
using NUnit.Framework;
4+
using RestSharp.Extensions;
5+
6+
namespace RestSharp.Tests.Extensions
7+
{
8+
[TestFixture]
9+
public class RSACryptoServiceProviderExtensionsTests
10+
{
11+
[Test]
12+
public void FromXmlStringImpl_GivenPrivateKeyXml_GivesSameResultAsDotNetImplementation()
13+
{
14+
const string samplePrivateKeyXml =
15+
"<RSAKeyValue><Modulus>twJgSXtGu3QQKComA/6wgcTPFS6cky+EHA+fCAZm+Suz0KpiYqvk4LHV+MQQvVy1TpWjpC1iXtEa5BfMS8zDLfrXaXA6RSZ3QEw8YfmmMrKDwUULIORgqcW8Uybalp5fMdbOieAQNXpOLNjnjPZVmFrQvB+CzfltYo82aEiOTjk=</Modulus><Exponent>AQAB</Exponent><P>8x4Omo3kOOExZP/XbtWLHlW7WfEtJNXIATzYlpOQAM1+mwJ7qBAP2umzudUdfXJECMKyv1e+eVeb0WatIsj+vw==</P><Q>wLTwSuM+KG57O4VTddyBSXRHLJvahfWlB1VettJvcqgQk2zK4XwoZU7POjq5fx6kfAUyAYaaxHfwKhKBIy1pBw==</Q><DP>F3LRs8R1u6q0qeonLDB6f42DSXSChyf7Z2sn9LX80KcBTBAcPyR1cwbRZ94PPxczSqkEtoHPBEMX60V883rxXw==</DP><DQ>UQ/LxLSygO94hyEeaoXHHM784Zbt5Uvfj6YpoV4D44cu8dThwtgnZfYw1Z2+Serp5gGJd3rXv610KT5/c/y2IQ==</DQ><InverseQ>jV3wG0+jRpbnkpYLBMVFmLlhJ68oZnpI+fbVnm5mBMr3Rzytz2HfgaGpmI6MY+ni9JV0pfntKNT6uo/Jji34gQ==</InverseQ><D>D4MZDEFxvmPZFr5z2HTXGzjGYMJBrUwiw4ojbbe1NLuakz5N9pUhYlZQj7R2wsY/6/hNFZZvNyA8SkcmHuqtRGyEmE9JOzRA5YhxkC6rfy9oTR2ybIrv9mUGU7P76PBPO2VQJdIIgAdTXMIz8o3IOStINpEkGWzptQ1yxZ8Apx0=</D></RSAKeyValue>";
16+
17+
using (var customBasedProvider = new RSACryptoServiceProvider())
18+
using (var dotnetBasedProvider = new RSACryptoServiceProvider())
19+
{
20+
RSACryptoServiceProviderExtensions.FromXmlStringImpl(customBasedProvider, samplePrivateKeyXml);
21+
dotnetBasedProvider.FromXmlString(samplePrivateKeyXml);
22+
23+
var dotnetBasedParameters = customBasedProvider.ExportParameters(true);
24+
var customBasedParameters = customBasedProvider.ExportParameters(true);
25+
26+
Assert.AreEqual(dotnetBasedParameters.D, customBasedParameters.D);
27+
Assert.AreEqual(dotnetBasedParameters.DP, customBasedParameters.DP);
28+
Assert.AreEqual(dotnetBasedParameters.DQ, customBasedParameters.DQ);
29+
Assert.AreEqual(dotnetBasedParameters.Exponent, customBasedParameters.Exponent);
30+
Assert.AreEqual(dotnetBasedParameters.InverseQ, customBasedParameters.InverseQ);
31+
Assert.AreEqual(dotnetBasedParameters.Modulus, customBasedParameters.Modulus);
32+
Assert.AreEqual(dotnetBasedParameters.P, customBasedParameters.P);
33+
Assert.AreEqual(dotnetBasedParameters.Q, customBasedParameters.Q);
34+
}
35+
}
36+
37+
[Test]
38+
public void FromXmlStringImpl_GivenInvalidPrivateKeyXml_ThrowsInvalidOperationException()
39+
{
40+
const string samplePrivateKeyXml =
41+
"<something></something>";
42+
43+
using (var provider = new RSACryptoServiceProvider())
44+
{
45+
var exception = Assert.Throws<InvalidOperationException>(() =>
46+
RSACryptoServiceProviderExtensions.FromXmlStringImpl(provider, samplePrivateKeyXml));
47+
Assert.AreEqual("Invalid XML RSA key.", exception.Message);
48+
}
49+
}
50+
51+
[Test]
52+
public void FromXmlStringImpl_GivenPrivateKeyXmlWithUnknownNode_ThrowsInvalidOperationException()
53+
{
54+
const string samplePrivateKeyXml =
55+
"<RSAKeyValue><pi>unexpected</pi></RSAKeyValue>";
56+
57+
using (var provider = new RSACryptoServiceProvider())
58+
{
59+
var exception = Assert.Throws<InvalidOperationException>(() =>
60+
RSACryptoServiceProviderExtensions.FromXmlStringImpl(provider, samplePrivateKeyXml));
61+
Assert.AreEqual("Unknown node name: pi", exception.Message);
62+
}
63+
}
64+
}
65+
}

RestSharp/Authenticators/OAuth/OAuthTools.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
using System.Runtime.Serialization;
44
using System.Security.Cryptography;
55
using System.Text;
6+
using System.Xml;
67
using RestSharp.Authenticators.OAuth.Extensions;
8+
using RestSharp.Extensions;
79

810
namespace RestSharp.Authenticators.OAuth
911
{
@@ -342,9 +344,9 @@ public static string GetSignature(OAuthSignatureMethod signatureMethod,
342344

343345
case OAuthSignatureMethod.RsaSha1:
344346
{
345-
using (var provider = new RSACryptoServiceProvider() { PersistKeyInCsp = false })
347+
using (var provider = new RSACryptoServiceProvider { PersistKeyInCsp = false })
346348
{
347-
provider.FromXmlString(unencodedConsumerSecret);
349+
provider.FromXmlString2(unencodedConsumerSecret);
348350

349351
SHA1Managed hasher = new SHA1Managed();
350352
byte[] hash = hasher.ComputeHash(encoding.GetBytes(signatureBase));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System;
2+
using System.Security.Cryptography;
3+
using System.Xml;
4+
5+
namespace RestSharp.Extensions
6+
{
7+
public static class RSACryptoServiceProviderExtensions
8+
{
9+
/// <summary>
10+
/// Imports the specified XML String into the crypto service provider
11+
/// </summary>
12+
/// <remarks>
13+
/// .NET Core 2.0 doesn't provide an implementation of RSACryptoServiceProvider.FromXmlString/ToXmlString, so we have to do it ourselves.
14+
/// Source: https://gist.github.com/Jargon64/5b172c452827e15b21882f1d76a94be4/
15+
/// </remarks>
16+
public static void FromXmlString2(this RSACryptoServiceProvider rsa, string xmlString)
17+
{
18+
#if !NETSTANDARD2_0
19+
rsa.FromXmlString(xmlString);
20+
#else
21+
FromXmlStringImpl(rsa, xmlString);
22+
#endif
23+
}
24+
25+
internal static void FromXmlStringImpl(RSACryptoServiceProvider rsa, string xmlString)
26+
{
27+
var parameters = new RSAParameters();
28+
29+
var xmlDoc = new XmlDocument();
30+
xmlDoc.LoadXml(xmlString);
31+
32+
if (!xmlDoc.DocumentElement.Name.Equals("RSAKeyValue"))
33+
{
34+
throw new InvalidOperationException("Invalid XML RSA key.");
35+
}
36+
37+
38+
foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
39+
{
40+
switch (node.Name)
41+
{
42+
case "Modulus":
43+
parameters.Modulus = Convert.FromBase64String(node.InnerText);
44+
break;
45+
case "Exponent":
46+
parameters.Exponent = Convert.FromBase64String(node.InnerText);
47+
break;
48+
case "P":
49+
parameters.P = Convert.FromBase64String(node.InnerText);
50+
break;
51+
case "Q":
52+
parameters.Q = Convert.FromBase64String(node.InnerText);
53+
break;
54+
case "DP":
55+
parameters.DP = Convert.FromBase64String(node.InnerText);
56+
break;
57+
case "DQ":
58+
parameters.DQ = Convert.FromBase64String(node.InnerText);
59+
break;
60+
case "InverseQ":
61+
parameters.InverseQ = Convert.FromBase64String(node.InnerText);
62+
break;
63+
case "D":
64+
parameters.D = Convert.FromBase64String(node.InnerText);
65+
break;
66+
default:
67+
throw new InvalidOperationException("Unknown node name: " + node.Name);
68+
}
69+
}
70+
71+
rsa.ImportParameters(parameters);
72+
}
73+
}
74+
}

0 commit comments

Comments
 (0)