Permalink
Browse files

OAuth modifications

  • Loading branch information...
configurator committed Aug 16, 2012
1 parent 5fe52f3 commit 5565224831f2263df30cd7f3d685ef58ccd48d7a
@@ -6,21 +6,23 @@
using System.Security.Cryptography;
using System.Text;
using Raven.Abstractions.Extensions;
-using Raven.Imports.Newtonsoft.Json;
+using Raven.Abstractions.Extensions.Internal;
namespace Raven.Abstractions.Connection
{
public static class OAuthHelper
{
public static class Keys
{
- public const string RSAPublicKey = "rsa key";
public const string EncryptedData = "data";
public const string APIKeyName = "api key name";
public const string Challenge = "challenge";
public const string Response = "response";
- public const string ChallengeTimestamp = "timestamp";
+ public const string RSAExponent = "exponent";
+ public const string RSAModulus = "modulus";
+
+ public const string ChallengeTimestamp = "pepper";
public const string ChallengeSalt = "salt";
public const int ChallengeSaltLength = 64;
@@ -30,22 +32,30 @@ public static class Keys
private static readonly RandomNumberGenerator rng = RandomNumberGenerator.Create();
private static readonly SHA1 sha1 = SHA1.Create();
- private static RSA rsa = null;
- private static Aes aes = null;
+ private static RSACryptoServiceProvider rsa = null;
+ private static AesCryptoServiceProvider aes = null;
- private static string rsaPublicKey = null;
+ private static string rsaExponent = null;
+ private static string rsaModulus = null;
public static void InitializeServerKeys()
{
- rsa = RSA.Create();
- aes = Aes.Create();
+ rsa = new RSACryptoServiceProvider();
+ aes = new AesCryptoServiceProvider();
+
+ var rsaParameters = rsa.ExportParameters(false);
+ rsaExponent = BytesToString(rsaParameters.Exponent);
+ rsaModulus = BytesToString(rsaParameters.Modulus);
+ }
- rsaPublicKey = SerializeRsaParameters(rsa.ExportParameters(false));
+ public static string RSAExponent
+ {
+ get { return rsaExponent; }
}
- public static string RSAPublicKey
+ public static string RSAModulus
{
- get { return rsaPublicKey; }
+ get { return rsaModulus; }
}
/**** Cryptography *****/
@@ -86,20 +96,37 @@ public static string DecryptSymmetric(string data)
public static string EncryptAssymetric(RSAParameters parameters, string data)
{
+ const int partLength = 50;
+
var bytes = Encoding.UTF8.GetBytes(data);
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(parameters);
- var result = rsa.EncryptValue(bytes);
- return BytesToString(result);
+
+ string result = "";
+ for (int index = 0; index < bytes.Length; index += partLength)
+ {
+ var partBytes = bytes.Skip(index).Take(partLength);
+ var part = rsa.Encrypt(partBytes.ToArray(), true);
+ result += BytesToString(part) + "&";
+ }
+ return result;
}
}
public static string DecryptAsymmetric(string data)
{
- var bytes = ParseBytes(data);
- var result = rsa.DecryptValue(bytes);
- return Encoding.UTF8.GetString(result);
+ var parts = data.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries);
+
+ var result = Enumerable.Empty<byte>();
+ foreach (var part in parts)
+ {
+ var partBytes = ParseBytes(part);
+ var decrypted = rsa.Decrypt(partBytes, true);
+ result = result.Concat(decrypted);
+ }
+
+ return Encoding.UTF8.GetString(result.ToArray());
}
/**** On the wire *****/
@@ -109,7 +136,7 @@ public static string DecryptAsymmetric(string data)
return data.Split(',')
.Select(item => item.Split(new[] { '=' }, 2))
.ToDictionary(
- item => (item.First() ?? "").Trim(),
+ item => (item.First()).Trim(),
item => (item.Skip(1).FirstOrDefault() ?? "").Trim()
);
}
@@ -144,7 +171,7 @@ public static string BytesToString(byte[] data)
public static DateTime? ParseDateTime(string data)
{
DateTime result;
- if (DateTime.TryParseExact(data, "O", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result))
+ if (DateTime.TryParseExact(data, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out result))
return result;
else
return null;
@@ -154,22 +181,5 @@ public static string DateTimeToString(DateTime data)
{
return data.ToString("O", CultureInfo.InvariantCulture);
}
-
- public static string SerializeRsaParameters(RSAParameters data)
- {
- using (var writer = new StringWriter())
- {
- new JsonSerializer().Serialize(writer, data);
- return writer.ToString();
- }
- }
-
- public static RSAParameters DeserializeRSAParameters(string data)
- {
- using (var reader = new StringReader(data))
- {
- return new JsonSerializer().Deserialize<RSAParameters>(reader);
- }
- }
}
}
@@ -32,6 +32,7 @@
#else
using System.Security.Cryptography;
+using Raven.Abstractions.Extensions.Internal;
#endif
@@ -252,22 +253,22 @@ public override void Dispose()
#if DEBUG
GC.SuppressFinalize(this);
#endif
-
+
var tasks = new List<Task>();
foreach (var databaseChange in databaseChanges)
{
var remoteDatabaseChanges = databaseChange.Value as RemoteDatabaseChanges;
- if(remoteDatabaseChanges != null)
+ if (remoteDatabaseChanges != null)
{
tasks.Add(remoteDatabaseChanges.DisposeAsync());
}
else
{
- using(databaseChange.Value as IDisposable){}
+ using (databaseChange.Value as IDisposable) { }
}
}
-
+
foreach (var replicationInformer in replicationInformers)
{
replicationInformer.Value.Dispose();
@@ -399,7 +400,7 @@ public override IDocumentStore Initialize()
}
#endif
-#if !NET35
+#if !NET35
if (Conventions.AsyncDocumentKeyGenerator == null && asyncDatabaseCommandsGenerator != null)
{
#if !SILVERLIGHT
@@ -459,7 +460,7 @@ private void InitializeSecurity()
};
#if !SILVERLIGHT
-
+
Conventions.HandleUnauthorizedResponse = (response) =>
{
if (ApiKey == null)
@@ -482,7 +483,7 @@ private void InitializeSecurity()
if (string.IsNullOrEmpty(oauthSource))
oauthSource = Url + "/OAuth/API-Key";
- return DoOAuthRequestAsync(oauthSource, null, null, 0);
+ return DoOAuthRequestAsync(oauthSource, null, null, null, 0);
};
#endif
#endif
@@ -491,26 +492,29 @@ private void InitializeSecurity()
#if !SILVERLIGHT
private Action<HttpWebRequest> DoOAuthRequest(string oauthSource)
{
- string serverRSAPublicKey = null;
+ string serverRSAExponent = null;
+ string serverRSAModulus = null;
string challenge = null;
// Note that at two tries will be needed in the normal case.
// The first try will get back a challenge,
// the second try will try authentication. If something goes wrong server-side though
// (e.g. the server was just rebooted or the challenge timed out for some reason), we
// might get a new challenge back, so we try a third time just in case.
- for (int tries = 0; ; tries++)
+ int tries = 0;
+ while (true)
{
- var authRequest = PrepareOAuthRequest(oauthSource, serverRSAPublicKey, challenge);
+ tries++;
+ var authRequest = PrepareOAuthRequest(oauthSource, serverRSAExponent, serverRSAModulus, challenge);
try
{
- using (var authResponse = authRequest.GetResponse())
- using (var stream = authResponse.GetResponseStreamWithHttpDecompression())
- using (var reader = new StreamReader(stream))
- {
- currentOauthToken = "Bearer " + reader.ReadToEnd();
- return (Action<HttpWebRequest>)(request => SetHeader(request.Headers, "Authorization", currentOauthToken));
+ using (var authResponse = authRequest.GetResponse())
+ using (var stream = authResponse.GetResponseStreamWithHttpDecompression())
+ using (var reader = new StreamReader(stream))
+ {
+ currentOauthToken = "Bearer " + reader.ReadToEnd();
+ return (Action<HttpWebRequest>)(request => SetHeader(request.Headers, "Authorization", currentOauthToken));
}
}
catch (WebException ex)
@@ -528,21 +532,22 @@ private Action<HttpWebRequest> DoOAuthRequest(string oauthSource)
throw;
var challengeDictionary = OAuthHelper.ParseDictionary(header.Substring(OAuthHelper.Keys.WWWAuthenticateHeaderKey.Length).Trim());
- serverRSAPublicKey = challengeDictionary.TryGetValue(OAuthHelper.Keys.RSAPublicKey);
+ serverRSAExponent = challengeDictionary.TryGetValue(OAuthHelper.Keys.RSAExponent);
+ serverRSAModulus = challengeDictionary.TryGetValue(OAuthHelper.Keys.RSAModulus);
challenge = challengeDictionary.TryGetValue(OAuthHelper.Keys.Challenge);
}
}
}
#if !NET35
- private Task<Action<HttpWebRequest>> DoOAuthRequestAsync(string oauthSource, string serverRSAPublicKey, string challenge, int tries)
- {
- var authRequest = PrepareOAuthRequest(oauthSource, serverRSAPublicKey, challenge);
- return Task<WebResponse>.Factory.FromAsync(authRequest.BeginGetResponse, authRequest.EndGetResponse, null)
- .AddUrlIfFaulting(authRequest.RequestUri)
- .ConvertSecurityExceptionToServerNotFound()
- .ContinueWith(task =>
- {
+ private Task<Action<HttpWebRequest>> DoOAuthRequestAsync(string oauthSource, string serverRSAExponent, string serverRSAModulus, string challenge, int tries)
+ {
+ var authRequest = PrepareOAuthRequest(oauthSource, serverRSAExponent, serverRSAModulus, challenge);
+ return Task<WebResponse>.Factory.FromAsync(authRequest.BeginGetResponse, authRequest.EndGetResponse, null)
+ .AddUrlIfFaulting(authRequest.RequestUri)
+ .ConvertSecurityExceptionToServerNotFound()
+ .ContinueWith(task =>
+ {
try
{
using (var stream = task.Result.GetResponseStreamWithHttpDecompression())
@@ -558,8 +563,8 @@ private Task<Action<HttpWebRequest>> DoOAuthRequestAsync(string oauthSource, str
// We've already tried three times and failed
throw;
- var authResponse = ((HttpWebResponse)ex.Response);
- if (authResponse.StatusCode != HttpStatusCode.Unauthorized)
+ var authResponse = ex.Response as HttpWebResponse;
+ if (authResponse == null || authResponse.StatusCode != HttpStatusCode.Unauthorized)
throw;
var header = authResponse.Headers[HttpResponseHeader.WwwAuthenticate];
@@ -569,7 +574,8 @@ private Task<Action<HttpWebRequest>> DoOAuthRequestAsync(string oauthSource, str
var challengeDictionary = OAuthHelper.ParseDictionary(header.Substring(OAuthHelper.Keys.WWWAuthenticateHeaderKey.Length).Trim());
return DoOAuthRequestAsync(oauthSource,
- challengeDictionary.TryGetValue(OAuthHelper.Keys.RSAPublicKey),
+ challengeDictionary.TryGetValue(OAuthHelper.Keys.RSAExponent),
+ challengeDictionary.TryGetValue(OAuthHelper.Keys.RSAModulus),
challengeDictionary.TryGetValue(OAuthHelper.Keys.Challenge),
tries + 1);
}
@@ -591,26 +597,35 @@ private static void SetHeader(WebHeaderCollection headers, string key, string va
}
#if !SILVERLIGHT
- private HttpWebRequest PrepareOAuthRequest(string oauthSource, string serverRSAPublicKey, string challenge)
+ private HttpWebRequest PrepareOAuthRequest(string oauthSource, string serverRSAExponent, string serverRSAModulus, string challenge)
{
var authRequest = (HttpWebRequest)WebRequest.Create(oauthSource);
authRequest.Headers["Accept-Encoding"] = "deflate,gzip";
authRequest.Headers["grant_type"] = "client_credentials";
authRequest.Accept = "application/json;charset=UTF-8";
+ authRequest.Method = "POST";
- if (!string.IsNullOrEmpty(serverRSAPublicKey) && !string.IsNullOrEmpty(challenge))
+ if (!string.IsNullOrEmpty(serverRSAExponent) && !string.IsNullOrEmpty(serverRSAModulus) && !string.IsNullOrEmpty(challenge))
{
- var rsaParameters = OAuthHelper.DeserializeRSAParameters(serverRSAPublicKey);
- var apiKeyParts = ApiKey.Split('/');
- var apiKeyName = apiKeyParts[0];
- var apiSecret = apiKeyParts[1];
+ var rsaParameters = new RSAParameters
+ {
+ Exponent = OAuthHelper.ParseBytes(serverRSAExponent),
+ Modulus = OAuthHelper.ParseBytes(serverRSAModulus)
+ };
+
+ var apiKeyParts = ApiKey.Split(new[] { '/' }, 2);
+ if (apiKeyParts.Length != 2)
+ throw new InvalidOperationException("Invalid API key");
+ var apiKeyName = apiKeyParts[0].Trim();
+ var apiSecret = apiKeyParts[1].Trim();
using (var stream = authRequest.GetRequestStream())
using (var writer = new StreamWriter(stream))
{
writer.Write(OAuthHelper.DictionaryToString(new Dictionary<string, string>
{
- { OAuthHelper.Keys.RSAPublicKey, serverRSAPublicKey },
+ { OAuthHelper.Keys.RSAExponent, serverRSAExponent },
+ { OAuthHelper.Keys.RSAModulus, serverRSAModulus },
{ OAuthHelper.Keys.EncryptedData, OAuthHelper.EncryptAssymetric(rsaParameters, OAuthHelper.DictionaryToString(new Dictionary<string,string> {
{ OAuthHelper.Keys.APIKeyName, apiKeyName },
{ OAuthHelper.Keys.Challenge, challenge },
@@ -619,9 +634,13 @@ private HttpWebRequest PrepareOAuthRequest(string oauthSource, string serverRSAP
}));
}
}
+ else
+ {
+ authRequest.ContentLength = 0;
+ }
if (authRequest.RequestUri.Scheme.Equals("https", StringComparison.InvariantCultureIgnoreCase) == false &&
- jsonRequestFactory.EnableBasicAuthenticationOverUnsecureHttpEvenThoughPasswordsWouldBeSentOverTheWireInClearTextToBeStolenByHackers == false &&
+ jsonRequestFactory.EnableBasicAuthenticationOverUnsecureHttpEvenThoughPasswordsWouldBeSentOverTheWireInClearTextToBeStolenByHackers == false &&
IsLocalHost(authRequest) == false)
throw new InvalidOperationException(BasicOAuthOverHttpError);
@@ -690,8 +709,8 @@ protected virtual void InitializeInternal()
};
#endif
}
-
-
+
+
public ReplicationInformer GetReplicationInformerForDatabase(string dbName = null)
{
var key = Url;
@@ -744,7 +763,7 @@ public override IDatabaseChanges Changes(string database = null)
{
AssertInitialized();
- return databaseChanges.GetOrAdd(database ?? DefaultDatabase,
+ return databaseChanges.GetOrAdd(database ?? DefaultDatabase,
CreateDatabaseChanges);
}
@@ -761,7 +780,7 @@ protected virtual IDatabaseChanges CreateDatabaseChanges(string database)
return new RemoteDatabaseChanges(dbUrl, credentials, jsonRequestFactory, Conventions, () => databaseChanges.Remove(database));
}
-
+
/// <summary>
/// Setup the context for aggressive caching.
/// </summary>
Oops, something went wrong.

0 comments on commit 5565224

Please sign in to comment.