1
1
using System ;
2
+ using System . Linq ;
2
3
using System . Collections . Generic ;
3
4
using System . Collections . Specialized ;
4
5
using System . Security . Cryptography ;
@@ -45,16 +46,63 @@ public bool Validate(string url, NameValueCollection parameters, string expected
45
46
/// <returns>true if the signature matches the result; false otherwise</returns>
46
47
public bool Validate ( string url , IDictionary < string , string > parameters , string expected )
47
48
{
48
- // check signature of url with and without port, since sig generation on back end is inconsistent
49
- var signatureWithoutPort = GetValidationSignature ( RemovePort ( url ) , parameters ) ;
50
- var signatureWithPort = GetValidationSignature ( AddPort ( url ) , parameters ) ;
51
- // If either url produces a valid signature, we accept the request as valid
52
- return SecureCompare ( signatureWithoutPort , expected ) || SecureCompare ( signatureWithPort , expected ) ;
53
- }
54
-
49
+ if ( string . IsNullOrEmpty ( url ) )
50
+ throw new ArgumentException ( "Parameter 'url' cannot be null or empty." ) ;
51
+ if ( string . IsNullOrEmpty ( expected ) )
52
+ throw new ArgumentException ( "Parameter 'expected' cannot be null or empty." ) ;
53
+
54
+ if ( parameters == null || parameters . Count == 0 )
55
+ {
56
+ var signature = GetValidationSignature ( url ) ;
57
+ if ( SecureCompare ( signature , expected ) ) return true ;
58
+
59
+ // check signature of url with and without port, since sig generation on back end is inconsistent
60
+ // If either url produces a valid signature, we accept the request as valid
61
+ url = GetUriVariation ( url ) ;
62
+ signature = GetValidationSignature ( url ) ;
63
+ if ( SecureCompare ( signature , expected ) ) return true ;
64
+ return false ;
65
+ }
66
+ else
67
+ {
68
+ var parameterStringBuilder = GetJoinedParametersStringBuilder ( parameters ) ;
69
+ parameterStringBuilder . Insert ( 0 , url ) ;
70
+ var signature = GetValidationSignature ( parameterStringBuilder . ToString ( ) ) ;
71
+ if ( SecureCompare ( signature , expected ) ) return true ;
72
+ parameterStringBuilder . Remove ( 0 , url . Length ) ;
73
+
74
+ // check signature of url with and without port, since sig generation on back end is inconsistent
75
+ // If either url produces a valid signature, we accept the request as valid
76
+ url = GetUriVariation ( url ) ;
77
+ parameterStringBuilder . Insert ( 0 , url ) ;
78
+ signature = GetValidationSignature ( parameterStringBuilder . ToString ( ) ) ;
79
+ if ( SecureCompare ( signature , expected ) ) return true ;
80
+
81
+ return false ;
82
+ }
83
+ }
84
+
85
+ private StringBuilder GetJoinedParametersStringBuilder ( IDictionary < string , string > parameters )
86
+ {
87
+ var keys = parameters . Keys . ToArray ( ) ;
88
+ Array . Sort ( keys , StringComparer . Ordinal ) ;
89
+
90
+ var b = new StringBuilder ( ) ;
91
+ foreach ( var key in keys )
92
+ {
93
+ b . Append ( key ) . Append ( parameters [ key ] ?? "" ) ;
94
+ }
95
+ return b ;
96
+ }
97
+
55
98
public bool Validate ( string url , string body , string expected )
56
99
{
57
- var paramString = new UriBuilder ( url ) . Query . TrimStart ( '?' ) ;
100
+ if ( string . IsNullOrEmpty ( url ) )
101
+ throw new ArgumentException ( "Parameter 'url' cannot be null or empty." ) ;
102
+ if ( string . IsNullOrEmpty ( expected ) )
103
+ throw new ArgumentException ( "Parameter 'expected' cannot be null or empty." ) ;
104
+
105
+ var paramString = new Uri ( url , UriKind . Absolute ) . Query . TrimStart ( '?' ) ;
58
106
var bodyHash = "" ;
59
107
foreach ( var param in paramString . Split ( '&' ) )
60
108
{
@@ -65,13 +113,13 @@ public bool Validate(string url, string body, string expected)
65
113
}
66
114
}
67
115
68
- return Validate ( url , new Dictionary < string , string > ( ) , expected ) && ValidateBody ( body , bodyHash ) ;
116
+ return Validate ( url , ( IDictionary < string , string > ) null , expected ) && ValidateBody ( body , bodyHash ) ;
69
117
}
70
118
71
119
public bool ValidateBody ( string rawBody , string expected )
72
120
{
73
121
var signature = _sha . ComputeHash ( Encoding . UTF8 . GetBytes ( rawBody ) ) ;
74
- return SecureCompare ( BitConverter . ToString ( signature ) . Replace ( "-" , "" ) . ToLower ( ) , expected ) ;
122
+ return SecureCompare ( BitConverter . ToString ( signature ) . Replace ( "-" , "" ) . ToLower ( ) , expected ) ;
75
123
}
76
124
77
125
private static IDictionary < string , string > ToDictionary ( NameValueCollection col )
@@ -81,27 +129,16 @@ private static IDictionary<string, string> ToDictionary(NameValueCollection col)
81
129
{
82
130
dict . Add ( k , col [ k ] ) ;
83
131
}
132
+
84
133
return dict ;
85
134
}
86
135
87
- private string GetValidationSignature ( string url , IDictionary < string , string > parameters )
136
+ private string GetValidationSignature ( string urlWithParameters )
88
137
{
89
- var b = new StringBuilder ( url ) ;
90
- if ( parameters != null )
91
- {
92
- var sortedKeys = new List < string > ( parameters . Keys ) ;
93
- sortedKeys . Sort ( StringComparer . Ordinal ) ;
94
-
95
- foreach ( var key in sortedKeys )
96
- {
97
- b . Append ( key ) . Append ( parameters [ key ] ?? "" ) ;
98
- }
99
- }
100
-
101
- var hash = _hmac . ComputeHash ( Encoding . UTF8 . GetBytes ( b . ToString ( ) ) ) ;
138
+ byte [ ] hash = _hmac . ComputeHash ( Encoding . UTF8 . GetBytes ( urlWithParameters ) ) ;
102
139
return Convert . ToBase64String ( hash ) ;
103
- }
104
-
140
+ }
141
+
105
142
private static bool SecureCompare ( string a , string b )
106
143
{
107
144
if ( a == null || b == null )
@@ -124,41 +161,55 @@ private static bool SecureCompare(string a, string b)
124
161
return mismatch == 0 ;
125
162
}
126
163
127
- private string RemovePort ( string url )
164
+ /// <summary>
165
+ /// Returns URL without port if given URL has port, returns URL with port if given URL has no port
166
+ /// </summary>
167
+ /// <param name="url"></param>
168
+ /// <returns></returns>
169
+ private string GetUriVariation ( string url )
128
170
{
129
- return SetPort ( url , - 1 ) ;
130
- }
171
+ var uri = new Uri ( url ) ;
172
+ var uriBuilder = new UriBuilder ( uri ) ;
173
+ var port = uri . GetComponents ( UriComponents . Port , UriFormat . UriEscaped ) ;
174
+ // if port already removed
175
+ if ( port == "" )
176
+ {
177
+ return SetPort ( url , uriBuilder , uriBuilder . Port ) ;
178
+ }
131
179
132
- private string AddPort ( string url )
133
- {
134
- var uri = new UriBuilder ( url ) ;
135
- return SetPort ( url , uri . Port ) ;
180
+ return SetPort ( url , uriBuilder , - 1 ) ;
136
181
}
137
182
138
- private string SetPort ( string url , int port )
183
+ private string SetPort ( string url , UriBuilder uri , int newPort )
139
184
{
140
- var uri = new UriBuilder ( url ) ;
141
- uri . Host = PreserveCase ( url , uri . Host ) ;
142
- if ( port == - 1 )
185
+ if ( newPort == - 1 )
143
186
{
144
- uri . Port = port ;
187
+ uri . Port = newPort ;
145
188
}
146
- else if ( ( port != 443 ) && ( port != 80 ) )
189
+ else if ( newPort != 443 && newPort != 80 )
147
190
{
148
- uri . Port = port ;
191
+ uri . Port = newPort ;
149
192
}
150
193
else
151
194
{
152
195
uri . Port = uri . Scheme == "https" ? 443 : 80 ;
153
196
}
197
+
198
+ var uriStringBuilder = new StringBuilder ( uri . ToString ( ) ) ;
199
+
200
+ var host = PreserveCase ( url , uri . Host ) ;
201
+ uriStringBuilder . Replace ( uri . Host , host ) ;
202
+
154
203
var scheme = PreserveCase ( url , uri . Scheme ) ;
155
- return uri . Uri . OriginalString . Replace ( uri . Scheme , scheme ) ;
156
- }
204
+ uriStringBuilder . Replace ( uri . Scheme , scheme ) ;
157
205
206
+ return uriStringBuilder . ToString ( ) ;
207
+ }
208
+
158
209
private string PreserveCase ( string url , string replacementString )
159
210
{
160
211
var startIndex = url . IndexOf ( replacementString , StringComparison . OrdinalIgnoreCase ) ;
161
212
return url . Substring ( startIndex , replacementString . Length ) ;
162
213
}
163
214
}
164
- }
215
+ }
0 commit comments