Imports System Imports System.Security.Cryptography Imports System.Collections Imports System.Collections.Generic Imports System.Text Imports System.Web Namespace OAuth Public Class OAuthBase '' ' List of know and used oauth parameters' names '' Protected Const OAuthVersion As String = "1.0" Protected Const OAuthParameterPrefix As String = "oauth_" Protected Const OAuthConsumerKeyKey As String = "oauth_consumer_key" Protected Const OAuthCallbackKey As String = "oauth_callback" Protected Const OAuthVersionKey As String = "oauth_version" Protected Const OAuthSignatureMethodKey As String = "oauth_signature_method" Protected Const OAuthSignatureKey As String = "oauth_signature" Protected Const OAuthTimestampKey As String = "oauth_timestamp" Protected Const OAuthNonceKey As String = "oauth_nonce" Protected Const OAuthTokenKey As String = "oauth_token" Protected Const OAuthTokenSecretKey As String = "oauth_token_secret" Protected Const HMACSHA1SignatureType As String = "HMAC-SHA1" Protected Const PlainTextSignatureType As String = "PLAINTEXT" Protected Const RSASHA1SignatureType As String = "RSA-SHA1" Protected random As New Random() Protected unreservedChars As String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~" ''' ''' Provides a predefined set of algorithms that are supported officially by the protocol ''' Public Enum SignatureTypes HMACSHA1 PLAINTEXT RSASHA1 End Enum ''' ''' Provides an internal structure to sort the query parameter ''' Protected Class QueryParameter Private name As String = Nothing Private value As String = Nothing Public Sub New(ByVal Name As String, ByVal value As String) Me.name = Name Me.value = value End Sub Public ReadOnly Property Nome() As String Get Return name End Get End Property Public ReadOnly Property Valor() As String Get Return value End Get End Property End Class ''' ''' Comparer class used to perform the sorting of the query parameters ''' Protected Class QueryParameterComparer #Region "IComparer Members" Public Function Compare(ByVal x As QueryParameter, ByVal y As QueryParameter) As Integer If (x.Nome = y.Nome) Then Return String.Compare(x.Valor, y.Valor) Else Return String.Compare(x.Nome, y.Nome) End If End Function #End Region End Class ''' ''' Helper function to compute a hash value ''' ''' The hashing algoirhtm used. If that algorithm needs some initialization, like HMAC and its derivatives, they should be initialized prior to passing it to this function ''' The data to hash ''' a Base64 string of the hash value Private Function ComputeHash(ByVal hashAlgorithm As HashAlgorithm, ByVal data As String) As String If (hashAlgorithm Is Nothing) Then Throw New ArgumentNullException("hashAlgorithm") End If If (String.IsNullOrEmpty(data)) Then Throw New ArgumentNullException("data") End If Dim dataBuffer As Byte() = System.Text.Encoding.ASCII.GetBytes(data) Dim hashBytes As Byte() = hashAlgorithm.ComputeHash(dataBuffer) Return Convert.ToBase64String(hashBytes) End Function ''' ''' Internal function to cut out all non oauth query string parameters (all parameters not begining with "oauth_") ''' ''' The query string part of the Url ''' A list of QueryParameter each containing the parameter name and value Private Function GetQueryParameters(ByVal parameters As String) As List(Of QueryParameter) If (parameters.StartsWith("?")) Then parameters = parameters.Remove(0, 1) End If Dim result As New List(Of QueryParameter) If (Not String.IsNullOrEmpty(parameters)) Then Dim p As String() = parameters.Split("&") For Each s As String In p If (Not String.IsNullOrEmpty(s) And Not s.StartsWith(OAuthParameterPrefix)) Then If (s.IndexOf("=") > -1) Then Dim temp As String() = s.Split("=") result.Add(New QueryParameter(temp(0), temp(1))) Else result.Add(New QueryParameter(s, String.Empty)) End If End If Next End If Return result End Function ''' ''' This is a different Url Encode implementation since the default .NET one outputs the percent encoding in lower case. ''' While this is not a problem with the percent encoding spec, it is used in upper case throughout OAuth ''' ''' The value to Url encode ''' Returns a Url encoded string Protected Function UrlEncode(ByVal value As String) As String Dim result As New StringBuilder() For Each symbol As Char In value If unreservedChars.IndexOf(symbol) <> -1 Then result.Append(symbol) Else result.Append("%" + String.Format("{0:X2}", Convert.ToInt32(symbol))) End If Next Return result.ToString() End Function ''' ''' Normalizes the request parameters according to the spec ''' ''' The list of parameters already sorted ''' a string representing the normalized parameters Protected Function NormalizeRequestParameters(ByVal parameters As IList(Of QueryParameter)) As String Dim sb As New StringBuilder() Dim i As Integer Dim p As QueryParameter = Nothing For i = 0 To parameters.Count - 1 p = parameters(i) sb.AppendFormat("{0}={1}", p.Nome, p.Valor) If (i < parameters.Count - 1) Then sb.Append("&") End If Next Return sb.ToString() End Function ''' ''' Generate the signature base that is used to produce the signature ''' ''' The full url that needs to be signed including its non OAuth url parameters ''' The consumer key ''' The token, if available. If not available pass null or an empty string ''' The token secret, if available. If not available pass null or an empty string ''' The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc) ''' The signature type. To use the default values use OAuthBase.SignatureTypes. ''' The signature base Public Function GenerateSignatureBase(ByVal url As Uri, ByVal consumerKey As String, ByVal token As String, ByVal tokenSecret As String, ByVal httpMethod As String, ByVal timeStamp As String, ByVal nonce As String, ByVal signatureType As String, ByRef normalizedUrl As String, ByRef normalizedRequestParameters As String) As String If token Is Nothing Then token = String.Empty End If If tokenSecret Is Nothing Then tokenSecret = String.Empty End If If String.IsNullOrEmpty(consumerKey) Then Throw New ArgumentException("consumerKey") End If If String.IsNullOrEmpty(httpMethod) Then Throw New ArgumentNullException("httpMethod") End If If String.IsNullOrEmpty(signatureType) Then Throw New ArgumentNullException("signatureType") End If normalizedUrl = Nothing normalizedRequestParameters = Nothing Dim parameters As List(Of QueryParameter) = GetQueryParameters(url.Query) parameters.Add(New QueryParameter(OAuthVersionKey, OAuthVersion)) parameters.Add(New QueryParameter(OAuthNonceKey, nonce)) parameters.Add(New QueryParameter(OAuthTimestampKey, timeStamp)) parameters.Add(New QueryParameter(OAuthSignatureMethodKey, signatureType)) parameters.Add(New QueryParameter(OAuthConsumerKeyKey, consumerKey)) If (Not String.IsNullOrEmpty(token)) Then parameters.Add(New QueryParameter(OAuthTokenKey, token)) End If 'parameters.Sort(New QueryParameterComparer()) normalizedUrl = String.Format("{0}://{1}", url.Scheme, url.Host) If (Not ((url.Scheme = "http" And url.Port = 80) Or (url.Scheme = "https" And url.Port = 443))) Then normalizedUrl += ":" + url.Port End If normalizedUrl += url.AbsolutePath normalizedRequestParameters = NormalizeRequestParameters(parameters) Dim signatureBase As New StringBuilder() signatureBase.AppendFormat("{0}&", httpMethod.ToUpper()) signatureBase.AppendFormat("{0}&", UrlEncode(normalizedUrl)) signatureBase.AppendFormat("{0}", UrlEncode(normalizedRequestParameters)) Return signatureBase.ToString() End Function ''' ''' Generate the signature value based on the given signature base and hash algorithm ''' ''' The signature based as produced by the GenerateSignatureBase method or by any other means ''' The hash algorithm used to perform the hashing. If the hashing algorithm requires initialization or a key it should be set prior to calling this method ''' A base64 string of the hash value Public Function GenerateSignatureUsingHash(ByVal signatureBase As String, ByVal hash As HashAlgorithm) As String Return ComputeHash(hash, signatureBase) End Function ''' ''' Generates a signature using the HMAC-SHA1 algorithm ''' ''' The full url that needs to be signed including its non OAuth url parameters ''' The consumer key ''' The consumer seceret ''' The token, if available. If not available pass null or an empty string ''' The token secret, if available. If not available pass null or an empty string ''' The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc) ''' A base64 string of the hash value Public Function GenerateSignature(ByVal url As Uri, ByVal consumerKey As String, ByVal consumerSecret As String, ByVal token As String, ByVal tokenSecret As String, ByVal httpMethod As String, ByVal timeStamp As String, ByVal nonce As String, ByRef normalizedUrl As String, ByRef normalizedRequestParameters As String) As String Return GenerateSignature(url, consumerKey, consumerSecret, token, tokenSecret, httpMethod, timeStamp, nonce, SignatureTypes.HMACSHA1, normalizedUrl, normalizedRequestParameters) End Function ''' ''' Generates a signature using the specified signatureType ''' ''' The full url that needs to be signed including its non OAuth url parameters ''' The consumer key ''' The consumer seceret ''' The token, if available. If not available pass null or an empty string ''' The token secret, if available. If not available pass null or an empty string ''' The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc) ''' The type of signature to use ''' A base64 string of the hash value Public Function GenerateSignature(ByVal url As Uri, ByVal consumerKey As String, ByVal consumerSecret As String, ByVal token As String, ByVal tokenSecret As String, ByVal httpMethod As String, ByVal timeStamp As String, ByVal nonce As String, ByVal signatureType As SignatureTypes, ByRef normalizedUrl As String, ByRef normalizedRequestParameters As String) As String normalizedUrl = Nothing normalizedRequestParameters = Nothing Select Case signatureType Case SignatureTypes.HMACSHA1 Dim signatureBase As String = GenerateSignatureBase(url, consumerKey, token, tokenSecret, httpMethod, timeStamp, nonce, HMACSHA1SignatureType, normalizedUrl, normalizedRequestParameters) Dim hmacsha1 As New HMACSHA1() hmacsha1.Key = Encoding.ASCII.GetBytes(String.Format("{0}&{1}", UrlEncode(consumerSecret), IIf(String.IsNullOrEmpty(tokenSecret), "", UrlEncode(tokenSecret)))) Return GenerateSignatureUsingHash(signatureBase, hmacsha1) Case SignatureTypes.PLAINTEXT Return UrlEncode(String.Format("{0}&{1}", consumerSecret, tokenSecret)) Case SignatureTypes.RSASHA1 Throw New NotImplementedException() Case Else Throw New ArgumentException("Unknown signature type", "signatureType") End Select End Function ''' ''' Generate the timestamp for the signature ''' ''' Public Function GenerateTimeStamp() As String ' Default implementation of UNIX time of the current UTC time Dim ts As TimeSpan = DateTime.UtcNow - New DateTime(1970, 1, 1, 0, 0, 0, 0) Return Convert.ToInt64(ts.TotalSeconds).ToString() End Function ''' ''' Generate a nonce ''' ''' Public Function GenerateNonce() As String ' Just a simple implementation of a random number between 123400 and 9999999 Return random.Next(123400, 9999999).ToString() End Function End Class End Namespace