Skip to content

Commit ce20004

Browse files
committed
HMAC authentication core routines
1 parent b293c9f commit ce20004

5 files changed

Lines changed: 162 additions & 53 deletions

File tree

src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,23 @@ public static bool IsNullOrDefault<T>(this T? value) where T : struct
9292
{
9393
return default(T).Equals(value.GetValueOrDefault());
9494
}
95+
96+
/// <summary>Converts bytes into a hex string.</summary>
97+
public static string ToHexString(this byte[] bytes, int length = 0)
98+
{
99+
if (bytes == null || bytes.Length <= 0)
100+
return "";
101+
102+
var sb = new StringBuilder();
103+
104+
foreach (byte b in bytes)
105+
{
106+
sb.Append(b.ToString("x2"));
107+
108+
if (length > 0 && sb.Length >= length)
109+
break;
110+
}
111+
return sb.ToString();
112+
}
95113
} // class
96114
}

src/Libraries/SmartStore.Core/Extensions/StringExtensions.cs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -259,15 +259,7 @@ public static string Hash(this string value, bool toBase64 = false, bool unicode
259259
}
260260
else
261261
{
262-
StringBuilder sb = new StringBuilder();
263-
264-
byte[] hashBytes = md5.ComputeHash(data);
265-
foreach (byte b in hashBytes)
266-
{
267-
sb.Append(b.ToString("x2").ToLower());
268-
}
269-
270-
return sb.ToString();
262+
return md5.ComputeHash(data).ToHexString().ToLower();
271263
}
272264
}
273265
}
@@ -907,12 +899,8 @@ public static string Sha(this string value)
907899
using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
908900
{
909901
byte[] data = Encoding.ASCII.GetBytes(value);
910-
StringBuilder sb = new StringBuilder();
911-
912-
foreach (byte b in sha1.ComputeHash(data))
913-
sb.Append(b.ToString("x2"));
914902

915-
return sb.ToString();
903+
return sha1.ComputeHash(data).ToHexString();
916904
}
917905
}
918906
return "";

src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@
320320
<Compile Include="WebApi\Configuration\WebApiConfigurationPublisher.cs" />
321321
<Compile Include="WebApi\GenericApiController.cs" />
322322
<Compile Include="WebApi\OData\WebApiQueryableAttribute.cs" />
323-
<Compile Include="WebApi\Security\WebApiPermissionProvider.cs" />
323+
<Compile Include="WebApi\Security\HmacAuthentication.cs" />
324324
<Compile Include="WebApi\SmartStoreWebApiHttpControllerActivator.cs" />
325325
<Compile Include="WebApi\SmartStoreWebApiHttpControllerSelector.cs" />
326326
<Compile Include="WebApi\WebApiCaching.cs" />
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
using System;
2+
using System.Security.Cryptography;
3+
using System.Text;
4+
5+
namespace SmartStore.Web.Framework.WebApi.Security
6+
{
7+
public class HmacAuthentication
8+
{
9+
private static readonly string _delimiterRepresentation = "\n";
10+
private static readonly string _scheme = "SmNetHmac";
11+
12+
public static string Scheme1 { get { return _scheme + "1"; } }
13+
public static string SignatureMethod { get { return "HMAC-SHA256"; } }
14+
15+
/// <summary>Creates two random unequal keys.</summary>
16+
public bool CreateKeys(out string key1, out string key2, int length = 32)
17+
{
18+
key1 = key2 = null;
19+
20+
using (var rng = RandomNumberGenerator.Create())
21+
{
22+
for (int i = 0; i < 9999; ++i)
23+
{
24+
byte[] data1 = new byte[length];
25+
byte[] data2 = new byte[length];
26+
27+
rng.GetNonZeroBytes(data1);
28+
rng.GetNonZeroBytes(data2);
29+
30+
key1 = data1.ToHexString(length).ToLower();
31+
key2 = data2.ToHexString(length).ToLower();
32+
33+
if (key1 != key2)
34+
break;
35+
}
36+
}
37+
return !string.IsNullOrWhiteSpace(key1) && !string.IsNullOrWhiteSpace(key2) && key1 != key2;
38+
}
39+
40+
/// <summary>Creates a base64 encoded hash for a content.</summary>
41+
public string CreateContentMd5Hash(byte[] content)
42+
{
43+
string result = "";
44+
if (content != null && content.Length > 0)
45+
{
46+
using (var md5 = MD5.Create())
47+
{
48+
byte[] hash = md5.ComputeHash(content);
49+
result = Convert.ToBase64String(hash);
50+
}
51+
}
52+
return result;
53+
}
54+
55+
/// <summary>Creates a base64 encoded HMAC-SHA256 signature.</summary>
56+
public string CreateSignature(string secretKey, string messageRepresentation)
57+
{
58+
if (string.IsNullOrWhiteSpace(secretKey) || string.IsNullOrWhiteSpace(messageRepresentation))
59+
return "";
60+
61+
string signature;
62+
var secretBytes = Encoding.UTF8.GetBytes(secretKey);
63+
var valueBytes = Encoding.UTF8.GetBytes(messageRepresentation);
64+
65+
using (var hmac = new HMACSHA256(secretBytes))
66+
{
67+
var hash = hmac.ComputeHash(valueBytes);
68+
signature = Convert.ToBase64String(hash);
69+
}
70+
return signature;
71+
}
72+
73+
/// <summary>Creates a message representation as follows:
74+
/// HTTP method\n +
75+
/// Content-MD5\n +
76+
/// Response content type (accept header)\n +
77+
/// Canonicalized URI\n
78+
/// ISO-8601 UTC timestamp including milliseconds (e.g. 2013-09-23T09:24:43.5395441Z)\n
79+
/// Public-Key
80+
/// </summary>
81+
public string CreateMessageRepresentation(WebApiRequestContext context, string contentMd5Hash, string timestamp)
82+
{
83+
if (context == null || !context.IsValid)
84+
return null;
85+
86+
string result = string.Join(_delimiterRepresentation,
87+
context.HttpMethod.ToLower(),
88+
contentMd5Hash ?? "",
89+
context.HttpAcceptType.ToLower(),
90+
context.Url.ToLower(),
91+
timestamp,
92+
context.PublicKey.ToLower()
93+
);
94+
return result;
95+
}
96+
97+
/// <summary>Creates the value for the authorization header entry.</summary>
98+
public string CreateAuthorizationHeader(string signature)
99+
{
100+
if (string.IsNullOrWhiteSpace(signature))
101+
return "";
102+
103+
return Scheme1 + " " + signature;
104+
}
105+
106+
/// <summary>Checks whether the authorization header is valid.</summary>
107+
public bool IsAuthorizationHeaderValid(string scheme, string signature)
108+
{
109+
return (!string.IsNullOrWhiteSpace(scheme) && scheme.StartsWith(_scheme) && !string.IsNullOrWhiteSpace(signature));
110+
}
111+
112+
/// <summary>Returns a validated, versioned scheme value.</summary>
113+
public string GetWwwAuthenticateScheme(string schemeConsumer)
114+
{
115+
if (!string.IsNullOrWhiteSpace(schemeConsumer) && schemeConsumer == Scheme1)
116+
{
117+
return schemeConsumer;
118+
}
119+
return Scheme1; // fallback to first version
120+
}
121+
}
122+
123+
124+
public enum HmacResult : int
125+
{
126+
Success = 0,
127+
FailedForUnknownReason,
128+
ApiUnavailable,
129+
InvalidAuthorizationHeader,
130+
InvalidSignature,
131+
InvalidTimestamp,
132+
TimestampOutOfPeriod,
133+
TimestampOlderThanLastRequest,
134+
MissingMessageRepresentationParameter,
135+
ContentMd5NotMatching,
136+
UserUnknown,
137+
UserDisabled,
138+
UserInvalid,
139+
UserHasNoPermission
140+
}
141+
}

src/Presentation/SmartStore.Web.Framework/WebApi/Security/WebApiPermissionProvider.cs

Lines changed: 0 additions & 38 deletions
This file was deleted.

0 commit comments

Comments
 (0)