diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2010-06-02 12:15:54 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2010-06-02 12:15:54 -0700 |
commit | f4dcdd88a5ec022a2ef0e69e52a987757d1d5696 (patch) | |
tree | b41c3c3be98715a1531657c71eb8c26af2bf4984 | |
parent | 2574b5f2a269220ad8a07e99394129c95ba2162e (diff) | |
download | DotNetOpenAuth-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.
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) { |