summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs12
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs2
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs13
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs5
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs41
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs7
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs2
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs2
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs7
9 files changed, 74 insertions, 17 deletions
diff --git a/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs b/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs
index 6a36a83..695ba74 100644
--- a/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs
+++ b/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs
@@ -13,13 +13,17 @@
internal class OAuth2AuthorizationServer : IAuthorizationServer {
private static readonly byte[] secret;
+ private static readonly RSAParameters asymmetricKey;
+
private readonly INonceStore nonceStore = new DatabaseNonceStore();
- static OAuth2AuthorizationServer()
- {
+ static OAuth2AuthorizationServer() {
+ // For this sample, we just generate random secrets.
RandomNumberGenerator crypto = new RNGCryptoServiceProvider();
secret = new byte[16];
crypto.GetBytes(secret);
+
+ asymmetricKey = new RSACryptoServiceProvider().ExportParameters(true);
}
#region Implementation of IAuthorizationServer
@@ -32,6 +36,10 @@
get { return this.nonceStore; }
}
+ public RSAParameters? AccessTokenSigningPrivateKey {
+ get { return asymmetricKey; }
+ }
+
public IConsumerDescription GetClient(string clientIdentifier) {
var consumerRow = Global.DataContext.OAuthConsumers.SingleOrDefault(
consumerCandidate => consumerCandidate.ConsumerKey == clientIdentifier);
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs
index d102ee6..4fa33ae 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs
@@ -49,7 +49,7 @@
tokenRequest.AuthorizationDescription = RefreshToken.Decode(this.AuthorizationServer.Secret, tokenRequest.CodeOrToken, message);
break;
case CodeOrTokenType.AccessToken:
- tokenRequest.AuthorizationDescription = AccessToken.Decode(this.AuthorizationServer.Secret, tokenRequest.CodeOrToken, message);
+ tokenRequest.AuthorizationDescription = AccessToken.Decode(this.AuthorizationServer.Secret, tokenRequest.CodeOrToken, this.AuthorizationServer.AccessTokenSigningPrivateKey, message);
break;
default:
throw ErrorUtilities.ThrowInternal("Unexpected value for CodeOrTokenType: " + tokenRequest.CodeOrTokenType);
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs
index 3d3ad44..d9a1149 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
+ using System.Security.Cryptography;
using System.Text;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Bindings;
@@ -18,13 +19,13 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
/// Initializes a new instance of the <see cref="AccessToken"/> class.
/// </summary>
/// <param name="channel">The channel.</param>
- private AccessToken(byte[] secret)
- : base(secret, true, true) {
+ private AccessToken(byte[] secret, RSAParameters? asymmetricSignatureKey = null)
+ : base(secret, asymmetricSignatureKey, true, true) {
Contract.Requires<ArgumentNullException>(secret != null, "channel");
}
- internal AccessToken(byte[] secret, IAuthorizationDescription authorization, TimeSpan? lifetime)
- : this(secret) {
+ internal AccessToken(byte[] secret, IAuthorizationDescription authorization, TimeSpan? lifetime, RSAParameters? asymmetricSignatureKey = null)
+ : this(secret, asymmetricSignatureKey) {
Contract.Requires<ArgumentNullException>(secret != null, "secret");
Contract.Requires<ArgumentNullException>(authorization != null, "authorization");
@@ -37,12 +38,12 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
internal TimeSpan? Lifetime { get; set; }
- internal static AccessToken Decode(byte[] secret, string value, IProtocolMessage containingMessage = null) {
+ internal static AccessToken Decode(byte[] secret, string value, RSAParameters? asymmetricSignatureKey = null, IProtocolMessage containingMessage = null) {
Contract.Requires<ArgumentNullException>(secret != null, "secret");
Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(value));
Contract.Ensures(Contract.Result<AccessToken>() != null);
- var self = new AccessToken(secret);
+ var self = new AccessToken(secret, asymmetricSignatureKey);
self.Decode(value, containingMessage);
return self;
}
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs
index 0c14e51..6c2707a 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs
@@ -9,13 +9,14 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
+ using System.Security.Cryptography;
using System.Text;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Bindings;
internal abstract class AuthorizationDataBag : DataBag, IAuthorizationDescription {
- protected AuthorizationDataBag(byte[] secret, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
- : base(secret, signed, encrypted, compressed, maximumAge, decodeOnceOnly) {
+ protected AuthorizationDataBag(byte[] secret, RSAParameters? asymmetricSignatureKey = null, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
+ : base(secret, asymmetricSignatureKey, signed, encrypted, compressed, maximumAge, decodeOnceOnly) {
}
[MessagePart]
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs
index eb7e069..72dbf79 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs
@@ -10,6 +10,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
using System.Diagnostics.Contracts;
using System.Linq;
using System.Security.Cryptography;
+ using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Web;
using DotNetOpenAuth.Messaging;
@@ -24,6 +25,10 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
private readonly byte[] secret;
+ private readonly RSACryptoServiceProvider asymmetricSigning;
+
+ private readonly HashAlgorithm hasherForAsymmetricSigning;
+
private readonly bool signed;
private readonly INonceStore decodeOnceOnly;
@@ -34,12 +39,18 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
private readonly bool compressed;
- protected DataBag(byte[] secret = null, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
+ protected DataBag(byte[] secret = null, RSAParameters? asymmetricSignatureKey = null, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
: base(Protocol.Default.Version) {
Contract.Requires<ArgumentException>(secret != null || (signed == null && encrypted == null), "A secret is required when signing or encrypting is required.");
Contract.Requires<ArgumentException>(signed || decodeOnceOnly == null, "A signature must be applied if this data is meant to be decoded only once.");
Contract.Requires<ArgumentException>(maximumAge.HasValue || decodeOnceOnly == null, "A maximum age must be given if a message can only be decoded once.");
+ if (asymmetricSignatureKey.HasValue) {
+ this.asymmetricSigning = new RSACryptoServiceProvider();
+ this.asymmetricSigning.ImportParameters(asymmetricSignatureKey.Value);
+ this.hasherForAsymmetricSigning = new SHA1CryptoServiceProvider();
+ }
+
if (secret != null) {
this.Hasher = new HMACSHA256(secret);
}
@@ -120,7 +131,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
if (signed) {
// Verify that the verification code was issued by this authorization server.
- ErrorUtilities.VerifyProtocol(string.Equals(this.Signature, this.CalculateSignature(), StringComparison.Ordinal), Protocol.bad_verification_code);
+ ErrorUtilities.VerifyProtocol(this.IsSignatureValid(), Protocol.bad_verification_code);
}
if (maximumAge.HasValue) {
@@ -146,6 +157,16 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
get { return this.GetType().Name; }
}
+ private bool IsSignatureValid() {
+ if (this.asymmetricSigning != null) {
+ byte[] bytesToSign = this.GetBytesToSign();
+ byte[] signature = Convert.FromBase64String(this.Signature);
+ return this.asymmetricSigning.VerifyData(bytesToSign, this.hasherForAsymmetricSigning, signature);
+ } else {
+ return string.Equals(this.Signature, this.CalculateSignature(), StringComparison.Ordinal);
+ }
+ }
+
/// <summary>
/// Calculates the signature for the data in this verification code.
/// </summary>
@@ -153,11 +174,25 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
private string CalculateSignature() {
Contract.Requires<InvalidOperationException>(this.Hasher != null);
+ byte[] bytesToSign = this.GetBytesToSign();
+ if (this.asymmetricSigning != null) {
+ byte[] signature = this.asymmetricSigning.SignData(bytesToSign, this.hasherForAsymmetricSigning);
+ return Convert.ToBase64String(signature);
+ } else {
+ return Convert.ToBase64String(this.Hasher.ComputeHash(bytesToSign));
+ }
+ }
+
+ private byte[] GetBytesToSign() {
// Sign the data, being sure to avoid any impact of the signature field itself.
var fields = MessageDescriptions.GetAccessor(this);
var fieldsCopy = fields.ToDictionary();
fieldsCopy.Remove("sig");
- return this.Hasher.ComputeHash(fieldsCopy);
+
+ var sortedData = new SortedDictionary<string, string>(fieldsCopy, StringComparer.OrdinalIgnoreCase);
+ string value = MessagingUtilities.CreateQueryString(sortedData);
+ byte[] bytesToSign = Encoding.UTF8.GetBytes(value);
+ return bytesToSign;
}
}
}
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs
index bcb6c05..4430d51 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs
@@ -69,7 +69,12 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
public virtual AccessTokenSuccessResponse PrepareAccessToken(IAccessTokenRequest request, TimeSpan? accessTokenLifetime = null, bool includeRefreshToken = true) {
Contract.Requires<ArgumentNullException>(request != null, "request");
- var accessToken = new AccessToken(this.AuthorizationServer.Secret, request.AuthorizationDescription, accessTokenLifetime);
+ var accessToken = new AccessToken(
+ this.AuthorizationServer.Secret,
+ request.AuthorizationDescription,
+ accessTokenLifetime,
+ this.AuthorizationServer.AccessTokenSigningPrivateKey);
+
var response = new AccessTokenSuccessResponse(request) {
Scope = request.AuthorizationDescription.Scope,
AccessToken = accessToken.Encode(),
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs
index d813453..b988bf3 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs
@@ -18,7 +18,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
/// </summary>
/// <param name="channel">The channel.</param>
private RefreshToken(byte[] secret)
- : base(secret, true, true) {
+ : base(secret, null, true, true) {
Contract.Requires<ArgumentNullException>(secret != null, "secret");
}
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs
index 17f7948..ef71934 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs
@@ -37,7 +37,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
/// </summary>
/// <param name="channel">The channel.</param>
private VerificationCode(byte[] secret, INonceStore nonceStore)
- : base(secret, true, true, false, MaximumMessageAge, nonceStore) {
+ : base(secret, null, true, true, false, MaximumMessageAge, nonceStore) {
Contract.Requires<ArgumentNullException>(secret != null, "secret");
Contract.Requires<ArgumentNullException>(nonceStore != null, "nonceStore");
}
diff --git a/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs b/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs
index 8d5bea4..83fbf6f 100644
--- a/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs
@@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OAuthWrap {
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
+ using System.Security.Cryptography;
using System.Text;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuthWrap.ChannelElements;
@@ -51,6 +52,8 @@ namespace DotNetOpenAuth.OAuthWrap {
byte[] Secret { get; }
+ RSAParameters? AccessTokenSigningPrivateKey { get; }
+
INonceStore VerificationCodeNonceStore { get; }
}
@@ -72,6 +75,10 @@ namespace DotNetOpenAuth.OAuthWrap {
}
}
+ RSAParameters? IAuthorizationServer.AccessTokenSigningPrivateKey {
+ get { throw new NotImplementedException(); }
+ }
+
INonceStore IAuthorizationServer.VerificationCodeNonceStore {
get {
Contract.Ensures(Contract.Result<INonceStore>() != null);