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