summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2008-09-22 21:40:20 -0700
committerAndrew <andrewarnott@gmail.com>2008-09-22 21:40:20 -0700
commit17971af4cbe5141367482600370f2a44d447d5d8 (patch)
treec1115f77bb60bed06589b1648b2f6c2b633da7c7 /src
parent9914b1f553fb55873f5de4f221495d65cc572b2d (diff)
downloadDotNetOpenAuth-17971af4cbe5141367482600370f2a44d447d5d8.zip
DotNetOpenAuth-17971af4cbe5141367482600370f2a44d447d5d8.tar.gz
DotNetOpenAuth-17971af4cbe5141367482600370f2a44d447d5d8.tar.bz2
Adding OAuth signing binding elements.
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOAuth/ChannelElements/HmacSha1SigningBindingElement.cs44
-rw-r--r--src/DotNetOAuth/ChannelElements/ITamperResistantOAuthMessage.cs3
-rw-r--r--src/DotNetOAuth/ChannelElements/OAuthChannel.cs20
-rw-r--r--src/DotNetOAuth/ChannelElements/PlainTextSigningBindingElement.cs44
-rw-r--r--src/DotNetOAuth/ChannelElements/RsaSha1SigningBindingElement.cs7
-rw-r--r--src/DotNetOAuth/ChannelElements/SigningBindingElementBase.cs96
-rw-r--r--src/DotNetOAuth/DotNetOAuth.csproj2
-rw-r--r--src/DotNetOAuth/UriUtil.cs2
8 files changed, 215 insertions, 3 deletions
diff --git a/src/DotNetOAuth/ChannelElements/HmacSha1SigningBindingElement.cs b/src/DotNetOAuth/ChannelElements/HmacSha1SigningBindingElement.cs
new file mode 100644
index 0000000..08c1c45
--- /dev/null
+++ b/src/DotNetOAuth/ChannelElements/HmacSha1SigningBindingElement.cs
@@ -0,0 +1,44 @@
+//-----------------------------------------------------------------------
+// <copyright file="HmacSha1SigningBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOAuth.Messaging;
+ using DotNetOAuth.Messaging.Bindings;
+
+ /// <summary>
+ /// A binding element that signs outgoing messages and verifies the signature on incoming messages.
+ /// </summary>
+ internal class HmacSha1SigningBindingElement : SigningBindingElementBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HmacSha1SigningBindingElement"/> class.
+ /// </summary>
+ internal HmacSha1SigningBindingElement()
+ : base("HMAC-SHA1") {
+ }
+
+ /// <summary>
+ /// Applies a signature to the message.
+ /// </summary>
+ /// <param name="message">The message to sign.</param>
+ protected override void Sign(ITamperResistantOAuthMessage message) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Validates the signature on a message.
+ /// Does NOT throw an exception on failing signature verification.
+ /// </summary>
+ /// <param name="message">The message with a signature to verify.</param>
+ /// <returns>True if the signature is valid. False otherwise.</returns>
+ protected override bool IsSignatureValid(ITamperResistantOAuthMessage message) {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOAuth/ChannelElements/ITamperResistantOAuthMessage.cs b/src/DotNetOAuth/ChannelElements/ITamperResistantOAuthMessage.cs
index c00ff8b..887efac 100644
--- a/src/DotNetOAuth/ChannelElements/ITamperResistantOAuthMessage.cs
+++ b/src/DotNetOAuth/ChannelElements/ITamperResistantOAuthMessage.cs
@@ -5,12 +5,13 @@
//-----------------------------------------------------------------------
namespace DotNetOAuth.ChannelElements {
+ using DotNetOAuth.Messaging;
using DotNetOAuth.Messaging.Bindings;
/// <summary>
/// An interface that OAuth messages implement to support signing.
/// </summary>
- internal interface ITamperResistantOAuthMessage : ITamperResistantProtocolMessage {
+ internal interface ITamperResistantOAuthMessage : IDirectedProtocolMessage, ITamperResistantProtocolMessage {
/// <summary>
/// Gets or sets the method used to sign the message.
/// </summary>
diff --git a/src/DotNetOAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOAuth/ChannelElements/OAuthChannel.cs
index 597f53d..683fdb8 100644
--- a/src/DotNetOAuth/ChannelElements/OAuthChannel.cs
+++ b/src/DotNetOAuth/ChannelElements/OAuthChannel.cs
@@ -12,7 +12,8 @@ namespace DotNetOAuth.ChannelElements {
using System.Text;
using System.Web;
using DotNetOAuth.Messaging;
-using DotNetOAuth.Messaging.Bindings;
+ using DotNetOAuth.Messaging.Bindings;
+ using DotNetOAuth.Messaging.Reflection;
/// <summary>
/// An OAuth-specific implementation of the <see cref="Channel"/> class.
@@ -70,6 +71,23 @@ using DotNetOAuth.Messaging.Bindings;
internal Uri Realm { get; set; }
/// <summary>
+ /// Encodes the names and values that are part of the message per OAuth 1.0 section 5.1.
+ /// </summary>
+ /// <param name="message">The message with data to encode.</param>
+ /// <returns>A dictionary of name-value pairs with their strings encoded.</returns>
+ internal static IDictionary<string, string> GetEncodedParameters(IProtocolMessage message) {
+ var encodedDictionary = new Dictionary<string, string>();
+ MessageDictionary messageDictionary = new MessageDictionary(message);
+ foreach (var pair in messageDictionary) {
+ var key = Uri.EscapeDataString(pair.Key);
+ var value = Uri.EscapeDataString(pair.Value);
+ encodedDictionary.Add(key, value);
+ }
+
+ return encodedDictionary;
+ }
+
+ /// <summary>
/// Searches an incoming HTTP request for data that could be used to assemble
/// a protocol request message.
/// </summary>
diff --git a/src/DotNetOAuth/ChannelElements/PlainTextSigningBindingElement.cs b/src/DotNetOAuth/ChannelElements/PlainTextSigningBindingElement.cs
new file mode 100644
index 0000000..c106289
--- /dev/null
+++ b/src/DotNetOAuth/ChannelElements/PlainTextSigningBindingElement.cs
@@ -0,0 +1,44 @@
+//-----------------------------------------------------------------------
+// <copyright file="PlainTextSigningBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOAuth.Messaging;
+ using DotNetOAuth.Messaging.Bindings;
+
+ /// <summary>
+ /// A binding element that signs outgoing messages and verifies the signature on incoming messages.
+ /// </summary>
+ internal class PlainTextSigningBindingElement : SigningBindingElementBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PlainTextSigningBindingElement"/> class.
+ /// </summary>
+ internal PlainTextSigningBindingElement()
+ : base("PLAINTEXT") {
+ }
+
+ /// <summary>
+ /// Applies a signature to the message.
+ /// </summary>
+ /// <param name="message">The message to sign.</param>
+ protected override void Sign(ITamperResistantOAuthMessage message) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Validates the signature on a message.
+ /// Does NOT throw an exception on failing signature verification.
+ /// </summary>
+ /// <param name="message">The message with a signature to verify.</param>
+ /// <returns>True if the signature is valid. False otherwise.</returns>
+ protected override bool IsSignatureValid(ITamperResistantOAuthMessage message) {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOAuth/ChannelElements/RsaSha1SigningBindingElement.cs b/src/DotNetOAuth/ChannelElements/RsaSha1SigningBindingElement.cs
index 9b25cd0..6bf6fdc 100644
--- a/src/DotNetOAuth/ChannelElements/RsaSha1SigningBindingElement.cs
+++ b/src/DotNetOAuth/ChannelElements/RsaSha1SigningBindingElement.cs
@@ -17,6 +17,13 @@ namespace DotNetOAuth.ChannelElements {
/// </summary>
internal class RsaSha1SigningBindingElement : SigningBindingElementBase {
/// <summary>
+ /// Initializes a new instance of the <see cref="RsaSha1SigningBindingElement"/> class.
+ /// </summary>
+ internal RsaSha1SigningBindingElement()
+ : base("RSA-SHA1") {
+ }
+
+ /// <summary>
/// Applies a signature to the message.
/// </summary>
/// <param name="message">The message to sign.</param>
diff --git a/src/DotNetOAuth/ChannelElements/SigningBindingElementBase.cs b/src/DotNetOAuth/ChannelElements/SigningBindingElementBase.cs
index 369c844..7b89f84 100644
--- a/src/DotNetOAuth/ChannelElements/SigningBindingElementBase.cs
+++ b/src/DotNetOAuth/ChannelElements/SigningBindingElementBase.cs
@@ -11,11 +11,25 @@ namespace DotNetOAuth.ChannelElements {
using System.Text;
using DotNetOAuth.Messaging;
using DotNetOAuth.Messaging.Bindings;
+ using DotNetOAuth.Messaging.Reflection;
/// <summary>
/// A binding element that signs outgoing messages and verifies the signature on incoming messages.
/// </summary>
internal abstract class SigningBindingElementBase : IChannelBindingElement {
+ /// <summary>
+ /// The signature method this binding element uses.
+ /// </summary>
+ private string signatureMethod;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SigningBindingElementBase"/> class.
+ /// </summary>
+ /// <param name="signatureMethod">The OAuth signature method that the binding element uses.</param>
+ internal SigningBindingElementBase(string signatureMethod) {
+ this.signatureMethod = signatureMethod;
+ }
+
#region IChannelBindingElement Members
/// <summary>
@@ -33,6 +47,7 @@ namespace DotNetOAuth.ChannelElements {
public bool PrepareMessageForSending(IProtocolMessage message) {
var signedMessage = message as ITamperResistantOAuthMessage;
if (signedMessage != null) {
+ signedMessage.SignatureMethod = this.signatureMethod;
this.Sign(signedMessage);
return true;
}
@@ -49,7 +64,13 @@ namespace DotNetOAuth.ChannelElements {
public bool PrepareMessageForReceiving(IProtocolMessage message) {
var signedMessage = message as ITamperResistantOAuthMessage;
if (signedMessage != null) {
+ if (!string.Equals(signedMessage.SignatureMethod, this.signatureMethod, StringComparison.Ordinal)) {
+ Logger.ErrorFormat("Expected signature method '{0}' but received message with a signature method of '{1}'.", this.signatureMethod, signedMessage.SignatureMethod);
+ throw new InvalidSignatureException(message);
+ }
+
if (!this.IsSignatureValid(signedMessage)) {
+ Logger.Error("Signature verification failed.");
throw new InvalidSignatureException(message);
}
@@ -62,6 +83,66 @@ namespace DotNetOAuth.ChannelElements {
#endregion
/// <summary>
+ /// Constructs the OAuth Signature Base String and returns the result.
+ /// </summary>
+ /// <param name="message">The message to derive the signature base string from.</param>
+ /// <param name="httpMethod">
+ /// The HTTP method to be used in sending the request.
+ /// </param>
+ /// <param name="additionalParameters">
+ /// Parameters outside the OAuth message that are appended to the query string
+ /// or included in a POST entity where the content-type is application/x-www-form-urlencoded.
+ /// </param>
+ /// <returns>The signature base string.</returns>
+ /// <remarks>
+ /// This method implements OAuth 1.0 section 9.1.
+ /// </remarks>
+ protected static string ConstructSignatureBaseString(ITamperResistantOAuthMessage message, string httpMethod, IDictionary<string, string> additionalParameters) {
+ if (String.IsNullOrEmpty(httpMethod)) {
+ throw new ArgumentNullException("httpMethod");
+ }
+
+ List<string> signatureBaseStringElements = new List<string>(3);
+
+ signatureBaseStringElements.Add(httpMethod.ToUpperInvariant());
+
+ UriBuilder endpoint = new UriBuilder(message.Recipient);
+ endpoint.Query = null;
+ endpoint.Fragment = null;
+ signatureBaseStringElements.Add(endpoint.Uri.AbsoluteUri);
+
+ // TODO: figure out whether parameters passed by other means in the same HttpWebRequest
+ // must also be signed.
+ var encodedDictionary = OAuthChannel.GetEncodedParameters(message);
+ encodedDictionary.Remove("oauth_signature");
+ var sortedKeyValueList = new List<KeyValuePair<string, string>>(encodedDictionary);
+ sortedKeyValueList.Sort(SignatureBaseStringParameterComparer);
+ StringBuilder paramBuilder = new StringBuilder();
+ foreach (var pair in sortedKeyValueList) {
+ if (paramBuilder.Length > 0) {
+ paramBuilder.Append("&");
+ }
+
+ paramBuilder.Append(pair.Key);
+ paramBuilder.Append('=');
+ paramBuilder.Append(pair.Value);
+ }
+
+ signatureBaseStringElements.Add(paramBuilder.ToString());
+
+ StringBuilder signatureBaseString = new StringBuilder();
+ foreach (string element in signatureBaseStringElements) {
+ if (signatureBaseString.Length > 0) {
+ signatureBaseString.Append("&");
+ }
+
+ signatureBaseString.Append(Uri.EscapeDataString(element));
+ }
+
+ return signatureBaseString.ToString();
+ }
+
+ /// <summary>
/// Applies a signature to the message.
/// </summary>
/// <param name="message">The message to sign.</param>
@@ -74,5 +155,20 @@ namespace DotNetOAuth.ChannelElements {
/// <param name="message">The message with a signature to verify.</param>
/// <returns>True if the signature is valid. False otherwise.</returns>
protected abstract bool IsSignatureValid(ITamperResistantOAuthMessage message);
+
+ /// <summary>
+ /// Sorts parameters according to OAuth signature base string rules.
+ /// </summary>
+ /// <param name="left">The first parameter to compare.</param>
+ /// <param name="right">The second parameter to compare.</param>
+ /// <returns>Negative, zero or positive.</returns>
+ private static int SignatureBaseStringParameterComparer(KeyValuePair<string, string> left, KeyValuePair<string, string> right) {
+ int result = string.CompareOrdinal(left.Key, right.Key);
+ if (result != 0) {
+ return result;
+ }
+
+ return string.CompareOrdinal(left.Value, right.Value);
+ }
}
}
diff --git a/src/DotNetOAuth/DotNetOAuth.csproj b/src/DotNetOAuth/DotNetOAuth.csproj
index baece49..d2e8b31 100644
--- a/src/DotNetOAuth/DotNetOAuth.csproj
+++ b/src/DotNetOAuth/DotNetOAuth.csproj
@@ -56,6 +56,8 @@
<Reference Include="System.XML" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="ChannelElements\PlainTextSigningBindingElement.cs" />
+ <Compile Include="ChannelElements\HmacSha1SigningBindingElement.cs" />
<Compile Include="Messages\SignedMessageBase.cs" />
<Compile Include="Messaging\Bindings\NonceMemoryStore.cs" />
<Compile Include="ChannelElements\SigningBindingElementBase.cs" />
diff --git a/src/DotNetOAuth/UriUtil.cs b/src/DotNetOAuth/UriUtil.cs
index 74f9e3e..10a42ad 100644
--- a/src/DotNetOAuth/UriUtil.cs
+++ b/src/DotNetOAuth/UriUtil.cs
@@ -13,7 +13,7 @@ namespace DotNetOAuth {
/// <summary>
/// Utility methods for working with URIs.
/// </summary>
- internal class UriUtil {
+ internal static class UriUtil {
/// <summary>
/// Tests a URI for the presence of an OAuth payload.
/// </summary>