summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2010-06-02 12:15:54 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2010-06-02 12:15:54 -0700
commitf4dcdd88a5ec022a2ef0e69e52a987757d1d5696 (patch)
treeb41c3c3be98715a1531657c71eb8c26af2bf4984
parent2574b5f2a269220ad8a07e99394129c95ba2162e (diff)
downloadDotNetOpenAuth-f4dcdd88a5ec022a2ef0e69e52a987757d1d5696.zip
DotNetOpenAuth-f4dcdd88a5ec022a2ef0e69e52a987757d1d5696.tar.gz
DotNetOpenAuth-f4dcdd88a5ec022a2ef0e69e52a987757d1d5696.tar.bz2
Access tokens are now asymmetrically encrypted for resource server consumption and signed from the auth server.
Refresh tokens and verification codes are signed and encrypted using a private auth server symmetric secret.
-rw-r--r--samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs2
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj1
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingUtilities.cs69
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs3
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs15
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs8
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs72
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs9
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs2
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs7
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs22
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs32
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/WebAppAuthorizationServer.cs9
13 files changed, 201 insertions, 50 deletions
diff --git a/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs b/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs
index 695ba74..ed70a15 100644
--- a/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs
+++ b/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs
@@ -36,7 +36,7 @@
get { return this.nonceStore; }
}
- public RSAParameters? AccessTokenSigningPrivateKey {
+ public RSAParameters AccessTokenSigningPrivateKey {
get { return asymmetricKey; }
}
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
index f4a02d4..7e2e46a 100644
--- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj
+++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
@@ -350,6 +350,7 @@ http://opensource.org/licenses/ms-pl.html
<Compile Include="OAuthWrap\Messages\WebServer\ResponseFormat.cs" />
<Compile Include="OAuthWrap\Messages\WebServer\ResponseFormatEncoder.cs" />
<Compile Include="OAuthWrap\ResourceServer.cs" />
+ <Compile Include="OAuthWrap\StandardAccessTokenAnalyzer.cs" />
<Compile Include="OAuthWrap\WebAppAuthorizationServer.cs" />
<Compile Include="OAuthWrap\WrapUtilities.cs" />
<Compile Include="OAuth\ChannelElements\ICombinedOpenIdProviderTokenManager.cs" />
diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
index ba1d4d8..c57c7ca 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
@@ -462,9 +462,9 @@ namespace DotNetOpenAuth.Messaging {
binaryWriter.Write(crypto.IV);
binaryWriter.Flush();
- using (var cryptoStream = new CryptoStream(ms, crypto.CreateEncryptor(), CryptoStreamMode.Write)) {
- cryptoStream.Write(buffer, 0, buffer.Length);
- }
+ var cryptoStream = new CryptoStream(ms, crypto.CreateEncryptor(), CryptoStreamMode.Write);
+ cryptoStream.Write(buffer, 0, buffer.Length);
+ cryptoStream.FlushFinalBlock();
return ms.ToArray();
}
@@ -522,6 +522,69 @@ namespace DotNetOpenAuth.Messaging {
return Encoding.UTF8.GetString(plainText);
}
+ internal static byte[] EncryptWithRandomSymmetricKey(this RSACryptoServiceProvider crypto, byte[] buffer) {
+ Contract.Requires<ArgumentNullException>(crypto != null, "crypto");
+ Contract.Requires<ArgumentNullException>(buffer != null, "buffer");
+
+ var symmetricCrypto = new RijndaelManaged {
+ Mode = CipherMode.CBC,
+ };
+
+ var encryptedStream = new MemoryStream();
+ var encryptedStreamWriter = new BinaryWriter(encryptedStream);
+
+ byte[] prequel = new byte[symmetricCrypto.Key.Length + symmetricCrypto.IV.Length];
+ Array.Copy(symmetricCrypto.Key, prequel, symmetricCrypto.Key.Length);
+ Array.Copy(symmetricCrypto.IV, 0, prequel, symmetricCrypto.Key.Length, symmetricCrypto.IV.Length);
+ byte[] encryptedPrequel = crypto.Encrypt(prequel, false);
+
+ encryptedStreamWriter.Write(encryptedPrequel.Length);
+ encryptedStreamWriter.Write(encryptedPrequel);
+ encryptedStreamWriter.Flush();
+
+ var cryptoStream = new CryptoStream(encryptedStream, symmetricCrypto.CreateEncryptor(), CryptoStreamMode.Write);
+ cryptoStream.Write(buffer, 0, buffer.Length);
+ cryptoStream.FlushFinalBlock();
+
+ return encryptedStream.ToArray();
+ }
+
+ internal static byte[] DecryptWithRandomSymmetricKey(this RSACryptoServiceProvider crypto, byte[] buffer) {
+ Contract.Requires<ArgumentNullException>(crypto != null, "crypto");
+ Contract.Requires<ArgumentNullException>(buffer != null, "buffer");
+
+ var encryptedStream = new MemoryStream(buffer);
+ var encryptedStreamReader = new BinaryReader(encryptedStream);
+
+ byte[] encryptedPrequel = encryptedStreamReader.ReadBytes(encryptedStreamReader.ReadInt32());
+ byte[] prequel = crypto.Decrypt(encryptedPrequel, false);
+
+ var symmetricCrypto = new RijndaelManaged {
+ Mode = CipherMode.CBC,
+ };
+
+ byte[] symmetricKey = new byte[symmetricCrypto.Key.Length];
+ byte[] symmetricIV = new byte[symmetricCrypto.IV.Length];
+ Array.Copy(prequel, symmetricKey, symmetricKey.Length);
+ Array.Copy(prequel, symmetricKey.Length, symmetricIV, 0, symmetricIV.Length);
+ symmetricCrypto.Key = symmetricKey;
+ symmetricCrypto.IV = symmetricIV;
+
+ // Allocate space for the decrypted buffer. We don't know how long it will be yet,
+ // but it will never be larger than the encrypted buffer.
+ var decryptedBuffer = new byte[encryptedStream.Length - encryptedStream.Position];
+ int actualDecryptedLength;
+
+ using (var cryptoStream = new CryptoStream(encryptedStream, symmetricCrypto.CreateDecryptor(), CryptoStreamMode.Read)) {
+ actualDecryptedLength = cryptoStream.Read(decryptedBuffer, 0, decryptedBuffer.Length);
+ }
+
+ // Create a new buffer with only the decrypted data.
+ var finalDecryptedBuffer = new byte[actualDecryptedLength];
+ Array.Copy(decryptedBuffer, finalDecryptedBuffer, actualDecryptedLength);
+ return finalDecryptedBuffer;
+ }
+
/// <summary>
/// Compresses a given buffer.
/// </summary>
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs
index 4fa33ae..a59e087 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs
@@ -48,9 +48,6 @@
case CodeOrTokenType.RefreshToken:
tokenRequest.AuthorizationDescription = RefreshToken.Decode(this.AuthorizationServer.Secret, tokenRequest.CodeOrToken, message);
break;
- case CodeOrTokenType.AccessToken:
- 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 d9a1149..e8cf3b7 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs
@@ -19,14 +19,12 @@ 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, RSAParameters? asymmetricSignatureKey = null)
- : base(secret, asymmetricSignatureKey, true, true) {
- Contract.Requires<ArgumentNullException>(secret != null, "channel");
+ private AccessToken(RSAParameters signingKey, RSAParameters encryptingKey)
+ : base(signingKey, encryptingKey) {
}
- internal AccessToken(byte[] secret, IAuthorizationDescription authorization, TimeSpan? lifetime, RSAParameters? asymmetricSignatureKey = null)
- : this(secret, asymmetricSignatureKey) {
- Contract.Requires<ArgumentNullException>(secret != null, "secret");
+ internal AccessToken(RSAParameters signingKey, RSAParameters encryptingKey, IAuthorizationDescription authorization, TimeSpan? lifetime)
+ : this(signingKey, encryptingKey) {
Contract.Requires<ArgumentNullException>(authorization != null, "authorization");
this.ClientIdentifier = authorization.ClientIdentifier;
@@ -38,12 +36,11 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
internal TimeSpan? Lifetime { get; set; }
- internal static AccessToken Decode(byte[] secret, string value, RSAParameters? asymmetricSignatureKey = null, IProtocolMessage containingMessage = null) {
- Contract.Requires<ArgumentNullException>(secret != null, "secret");
+ internal static AccessToken Decode(RSAParameters signingKey, RSAParameters encryptingKey, string value, IProtocolMessage containingMessage = null) {
Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(value));
Contract.Ensures(Contract.Result<AccessToken>() != null);
- var self = new AccessToken(secret, asymmetricSignatureKey);
+ var self = new AccessToken(signingKey, encryptingKey);
self.Decode(value, containingMessage);
return self;
}
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs
index 6c2707a..7be544a 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs
@@ -15,8 +15,12 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
using DotNetOpenAuth.Messaging.Bindings;
internal abstract class AuthorizationDataBag : DataBag, IAuthorizationDescription {
- 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) {
+ 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(RSAParameters? signingKey = null, RSAParameters? encryptingKey = null, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
+ : base(signingKey, encryptingKey, compressed, maximumAge, decodeOnceOnly) {
}
[MessagePart]
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs
index 72dbf79..5fc3e83 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs
@@ -27,10 +27,14 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
private readonly RSACryptoServiceProvider asymmetricSigning;
+ private readonly RSACryptoServiceProvider asymmetricEncrypting;
+
private readonly HashAlgorithm hasherForAsymmetricSigning;
private readonly bool signed;
+ protected HashAlgorithm hasher;
+
private readonly INonceStore decodeOnceOnly;
private readonly TimeSpan? maximumAge;
@@ -39,23 +43,11 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
private readonly bool compressed;
- protected DataBag(byte[] secret = null, RSAParameters? asymmetricSignatureKey = null, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
+ protected DataBag(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);
- }
-
- this.secret = secret;
this.signed = signed;
this.maximumAge = maximumAge;
this.decodeOnceOnly = decodeOnceOnly;
@@ -63,7 +55,31 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
this.compressed = compressed;
}
- protected HashAlgorithm Hasher { get; set; }
+ protected DataBag(RSAParameters? signingKey = null, RSAParameters? encryptingKey = null, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
+ : this(signingKey.HasValue, encryptingKey.HasValue, compressed, maximumAge, decodeOnceOnly) {
+ if (signingKey.HasValue) {
+ this.asymmetricSigning = new RSACryptoServiceProvider();
+ this.asymmetricSigning.ImportParameters(signingKey.Value);
+ }
+
+ if (encryptingKey.HasValue) {
+ this.asymmetricEncrypting = new RSACryptoServiceProvider();
+ this.asymmetricEncrypting.ImportParameters(encryptingKey.Value);
+ }
+
+ this.hasherForAsymmetricSigning = new SHA1CryptoServiceProvider();
+ }
+
+ protected DataBag(byte[] secret = null, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
+ : this(signed, encrypted, compressed, maximumAge, decodeOnceOnly) {
+ Contract.Requires<ArgumentException>(secret != null || (signed == null && encrypted == null), "A secret is required when signing or encrypting is required.");
+
+ if (secret != null) {
+ this.hasher = new HMACSHA256(secret);
+ }
+
+ this.secret = secret;
+ }
[MessagePart("sig")]
private string Signature { get; set; }
@@ -97,7 +113,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
}
if (encrypted) {
- encoded = MessagingUtilities.Encrypt(encoded, this.secret);
+ encoded = this.Encrypt(encoded);
}
return Convert.ToBase64String(encoded);
@@ -109,7 +125,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
byte[] encoded = Convert.FromBase64String(value);
if (encrypted) {
- encoded = MessagingUtilities.Decrypt(encoded, this.secret);
+ encoded = this.Decrypt(encoded);
}
if (compressed) {
@@ -172,14 +188,14 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
/// </summary>
/// <returns>The calculated signature.</returns>
private string CalculateSignature() {
- Contract.Requires<InvalidOperationException>(this.Hasher != null);
+ Contract.Requires<InvalidOperationException>(this.asymmetricSigning != null || 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));
+ return Convert.ToBase64String(this.hasher.ComputeHash(bytesToSign));
}
}
@@ -194,5 +210,25 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
byte[] bytesToSign = Encoding.UTF8.GetBytes(value);
return bytesToSign;
}
+
+ private byte[] Encrypt(byte[] value) {
+ Contract.Requires<InvalidOperationException>(this.asymmetricEncrypting != null || this.secret != null);
+
+ if (this.asymmetricEncrypting != null) {
+ return this.asymmetricEncrypting.EncryptWithRandomSymmetricKey(value);
+ } else {
+ return MessagingUtilities.Encrypt(value, this.secret);
+ }
+ }
+
+ private byte[] Decrypt(byte[] value) {
+ Contract.Requires<InvalidOperationException>(this.asymmetricEncrypting != null || this.secret != null);
+
+ if (this.asymmetricEncrypting != null) {
+ return this.asymmetricEncrypting.DecryptWithRandomSymmetricKey(value);
+ } else {
+ return MessagingUtilities.Decrypt(value, this.secret);
+ }
+ }
}
}
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs
index 4430d51..9bf95b7 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs
@@ -18,6 +18,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.Messaging.Reflection;
using DotNetOpenAuth.OAuthWrap.Messages;
+using System.Security.Cryptography;
/// <summary>
/// The channel for the OAuth WRAP protocol.
@@ -66,14 +67,14 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
/// <value>The authorization server. Will be null for channels serving clients.</value>
public IAuthorizationServer AuthorizationServer { get; set; }
- public virtual AccessTokenSuccessResponse PrepareAccessToken(IAccessTokenRequest request, TimeSpan? accessTokenLifetime = null, bool includeRefreshToken = true) {
+ public virtual AccessTokenSuccessResponse PrepareAccessToken(IAccessTokenRequest request, RSAParameters accessTokenEncryptingPublicKey, TimeSpan? accessTokenLifetime = null, bool includeRefreshToken = true) {
Contract.Requires<ArgumentNullException>(request != null, "request");
var accessToken = new AccessToken(
- this.AuthorizationServer.Secret,
+ this.AuthorizationServer.AccessTokenSigningPrivateKey,
+ accessTokenEncryptingPublicKey,
request.AuthorizationDescription,
- accessTokenLifetime,
- this.AuthorizationServer.AccessTokenSigningPrivateKey);
+ accessTokenLifetime);
var response = new AccessTokenSuccessResponse(request) {
Scope = request.AuthorizationDescription.Scope,
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs
index b988bf3..d813453 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, null, true, true) {
+ : base(secret, 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 ef71934..ecdd2b3 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs
@@ -7,10 +7,13 @@
namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
using System;
using System.Diagnostics.Contracts;
+ using System.Security.Cryptography;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Bindings;
internal class VerificationCode : AuthorizationDataBag {
+ private HashAlgorithm hasher = new SHA256Managed();
+
/// <summary>
/// Initializes a new instance of the <see cref="VerificationCode"/> class.
/// </summary>
@@ -37,7 +40,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
/// </summary>
/// <param name="channel">The channel.</param>
private VerificationCode(byte[] secret, INonceStore nonceStore)
- : base(secret, null, true, true, false, MaximumMessageAge, nonceStore) {
+ : base(secret, true, true, false, MaximumMessageAge, nonceStore) {
Contract.Requires<ArgumentNullException>(secret != null, "secret");
Contract.Requires<ArgumentNullException>(nonceStore != null, "nonceStore");
}
@@ -69,7 +72,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
}
private string CalculateCallbackHash(Uri callback) {
- return this.Hasher.ComputeHash(callback.AbsoluteUri);
+ return this.hasher.ComputeHash(callback.AbsoluteUri);
}
}
}
diff --git a/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs b/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs
index 83fbf6f..e43f6c8 100644
--- a/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs
@@ -4,8 +4,6 @@
// </copyright>
//-----------------------------------------------------------------------
-using DotNetOpenAuth.Messaging.Bindings;
-
namespace DotNetOpenAuth.OAuthWrap {
using System;
using System.Collections.Generic;
@@ -13,6 +11,7 @@ namespace DotNetOpenAuth.OAuthWrap {
using System.Linq;
using System.Security.Cryptography;
using System.Text;
+ using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuthWrap.ChannelElements;
@@ -50,9 +49,24 @@ namespace DotNetOpenAuth.OAuthWrap {
/// </remarks>
bool IsAuthorizationValid(IAuthorizationDescription authorization);
+ /// <summary>
+ /// Gets the secret used to symmetrically encrypt and sign verification codes and refresh tokens.
+ /// </summary>
+ /// <remarks>
+ /// This secret should be kept strictly confidential in the authorization server(s)
+ /// and NOT shared with the resource server. Anyone with this secret can mint
+ /// tokens to essentially grant themselves access to anything they want.
+ /// </remarks>
byte[] Secret { get; }
- RSAParameters? AccessTokenSigningPrivateKey { get; }
+ /// <summary>
+ /// Gets the asymmetric private key to use for signing access tokens.
+ /// </summary>
+ /// <remarks>
+ /// The public key in the private/public key pair will be used by the resource
+ /// servers to validate that the access token is minted by a trusted authorization server.
+ /// </remarks>
+ RSAParameters AccessTokenSigningPrivateKey { get; }
INonceStore VerificationCodeNonceStore { get; }
}
@@ -75,7 +89,7 @@ namespace DotNetOpenAuth.OAuthWrap {
}
}
- RSAParameters? IAuthorizationServer.AccessTokenSigningPrivateKey {
+ RSAParameters IAuthorizationServer.AccessTokenSigningPrivateKey {
get { throw new NotImplementedException(); }
}
diff --git a/src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs b/src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs
new file mode 100644
index 0000000..5eaa1e5
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs
@@ -0,0 +1,32 @@
+//-----------------------------------------------------------------------
+// <copyright file="StandardAccessTokenAnalyzer.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Security.Cryptography;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuthWrap.ChannelElements;
+
+ internal class StandardAccessTokenAnalyzer : IAccessTokenAnalyzer {
+ internal StandardAccessTokenAnalyzer() {
+ }
+
+ internal RSAParameters AuthorizationServerPublicSigningKey { get; set; }
+
+ internal RSAParameters ResourceServerPrivateEncryptionKey { get; set; }
+
+ public bool TryValidateAccessToken(string accessToken, out string user, out string scope) {
+ var token = AccessToken.Decode(this.AuthorizationServerPublicSigningKey, this.ResourceServerPrivateEncryptionKey, accessToken);
+ user = token.User;
+ scope = token.Scope;
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/WebAppAuthorizationServer.cs b/src/DotNetOpenAuth/OAuthWrap/WebAppAuthorizationServer.cs
index 837d571..6dda224 100644
--- a/src/DotNetOpenAuth/OAuthWrap/WebAppAuthorizationServer.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/WebAppAuthorizationServer.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OAuthWrap {
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
+ using System.Security.Cryptography;
using System.Text;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuthWrap.Messages;
@@ -67,7 +68,9 @@ namespace DotNetOpenAuth.OAuthWrap {
var request = ReadAccessTokenRequest(httpRequestInfo);
if (request != null)
{
- response = PrepareAccessTokenResponse(request);
+ // This convenience method only encrypts access tokens assuming that this auth server
+ // doubles as the resource server.
+ response = PrepareAccessTokenResponse(request, this.AuthorizationServer.AccessTokenSigningPrivateKey);
return true;
}
@@ -110,11 +113,11 @@ namespace DotNetOpenAuth.OAuthWrap {
return request;
}
- internal AccessTokenSuccessResponse PrepareAccessTokenResponse(WebAppAccessTokenRequest request) {
+ internal AccessTokenSuccessResponse PrepareAccessTokenResponse(WebAppAccessTokenRequest request, RSAParameters resourceServerPublicKey) {
Contract.Requires<ArgumentNullException>(request != null, "request");
Contract.Ensures(Contract.Result<AccessTokenSuccessResponse>() != null);
- return this.OAuthChannel.PrepareAccessToken(request);
+ return this.OAuthChannel.PrepareAccessToken(request, resourceServerPublicKey);
}
protected Uri GetCallback(WebAppRequest authorizationRequest) {