VB.NET OAuth library
The following code listing contains the OAuth library used in the VB.NET code example.
Imports System.Security.Cryptography Imports System.Collections.Generic Imports System.Text Imports System.Web Namespace OAuth ' Downloaded from http://oauth.googlecode.com/svn/code/csharp/OAuthBase.cs ' Converted to VB.NET Public Class OAuthBase ''' <summary> ''' Provides a predefined set of algorithms that are supported officially by the protocol ''' </summary> Public Enum SignatureTypes HMACSHA1 PLAINTEXT RSASHA1 End Enum ''' <summary> ''' Provides an internal structure to sort the query parameter ''' </summary> Protected Class QueryParameter Private m_name As String = Nothing Private m_value As String = Nothing Public Sub New(name As String, value As String) Me.m_name = name Me.m_value = value End Sub Public ReadOnly Property Name() As String Get Return m_name End Get End Property Public ReadOnly Property Value() As String Get Return m_value End Get End Property End Class ''' <summary> ''' Comparer class used to perform the sorting of the query parameters ''' </summary> Protected Class QueryParameterComparer Implements IComparer(Of QueryParameter) #Region "IComparer<QueryParameter> Members" Public Function Compare(x As QueryParameter, y As QueryParameter) As Integer Implements IComparer _ (Of QueryParameter) _ .Compare If x.Name = y.Name Then Return String.Compare(x.Value, y.Value) Else Return String.Compare(x.Name, y.Name) End If End Function #End Region End Class Protected Const OAuthVersion As String = "1.0" Protected Const OAuthParameterPrefix As String = "oauth_" ' ' List of know and used oauth parameters' names ' 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 Shared unreservedChars As String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~" ''' <summary> ''' Helper function to compute a hash value ''' </summary> ''' <param name="hashAlgorithm">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</param> ''' <param name="data">The data to hash</param> ''' <returns>a Base64 string of the hash value</returns> Private Function ComputeHash(hashAlgorithm As HashAlgorithm, 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 ''' <summary> ''' Internal function to cut out all non oauth query string parameters (all parameters not begining with "oauth_") ''' </summary> ''' <param name="parameters">The query string part of the Url</param> ''' <returns>A list of QueryParameter each containing the parameter name and value</returns> Private Function GetQueryParameters(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("&"c) For Each s As String In p If Not String.IsNullOrEmpty(s) AndAlso Not s.StartsWith(OAuthParameterPrefix) Then If s.IndexOf("="c) > -1 Then Dim temp As String() = s.Split("="c) 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 ''' <summary> ''' 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 ''' </summary> ''' <param name="value">The value to Url encode</param> ''' <returns>Returns a Url encoded string</returns> Public Shared Function UrlEncode(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("%"c & [String].Format("{0:X2}", AscW(symbol))) End If Next Return result.ToString() End Function ''' <summary> ''' Normalizes the request parameters according to the spec ''' </summary> ''' <param name="parameters">The list of parameters already sorted</param> ''' <returns>a string representing the normalized parameters</returns> Protected Function NormalizeRequestParameters(parameters As IList(Of QueryParameter)) As String Dim sb As New StringBuilder() Dim p As QueryParameter = Nothing For i As Integer = 0 To parameters.Count - 1 p = parameters(i) sb.AppendFormat("{0}={1}", p.Name, p.Value) If i < parameters.Count - 1 Then sb.Append("&") End If Next Return sb.ToString() End Function ''' <summary> ''' Generate the signature base that is used to produce the signature ''' </summary> ''' <param name="url">The full url that needs to be signed including its non OAuth url parameters</param> ''' <param name="consumerKey">The consumer key</param> ''' <param name="token">The token, if available. If not available pass null or an empty string</param> ''' <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string</param> ''' <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)</param> ''' <param name="signatureType">The signature type. To use the default values use <see cref="OAuthBase.SignatureTypes">OAuthBase.SignatureTypes</see>.</param> ''' <returns>The signature base</returns> Public Function GenerateSignatureBase(url As Uri, consumerKey As String, token As String, tokenSecret As String, httpMethod As String, timeStamp As String, _ nonce As String, 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 ArgumentNullException("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" AndAlso url.Port = 80) OrElse (url.Scheme = "https" AndAlso 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 ''' <summary> ''' Generate the signature value based on the given signature base and hash algorithm ''' </summary> ''' <param name="signatureBase">The signature based as produced by the GenerateSignatureBase method or by any other means</param> ''' <param name="hash">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</param> ''' <returns>A base64 string of the hash value</returns> Public Function GenerateSignatureUsingHash(signatureBase As String, hash As HashAlgorithm) As String Return ComputeHash(hash, signatureBase) End Function ''' <summary> ''' Generates a signature using the HMAC-SHA1 algorithm ''' </summary> ''' <param name="url">The full url that needs to be signed including its non OAuth url parameters</param> ''' <param name="consumerKey">The consumer key</param> ''' <param name="consumerSecret">The consumer seceret</param> ''' <param name="token">The token, if available. If not available pass null or an empty string</param> ''' <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string</param> ''' <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)</param> ''' <returns>A base64 string of the hash value</returns> Public Function GenerateSignature(url As Uri, consumerKey As String, consumerSecret As String, token As String, tokenSecret As String, httpMethod As String, _ timeStamp As String, 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 ''' <summary> ''' Generates a signature using the specified signatureType ''' </summary> ''' <param name="url">The full url that needs to be signed including its non OAuth url parameters</param> ''' <param name="consumerKey">The consumer key</param> ''' <param name="consumerSecret">The consumer seceret</param> ''' <param name="token">The token, if available. If not available pass null or an empty string</param> ''' <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string</param> ''' <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)</param> ''' <param name="signatureType">The type of signature to use</param> ''' <returns>A base64 string of the hash value</returns> Public Function GenerateSignature(url As Uri, consumerKey As String, consumerSecret As String, token As String, tokenSecret As String, httpMethod As String, _ timeStamp As String, nonce As String, signatureType As SignatureTypes, ByRef normalizedUrl As String, ByRef normalizedRequestParameters As String) As String normalizedUrl = Nothing normalizedRequestParameters = Nothing Select Case signatureType Case SignatureTypes.PLAINTEXT Return HttpUtility.UrlEncode(String.Format("{0}&{1}", consumerSecret, tokenSecret)) 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), If(String.IsNullOrEmpty(tokenSecret), "", UrlEncode(tokenSecret)))) Return GenerateSignatureUsingHash(signatureBase, hmacsha1) Case SignatureTypes.RSASHA1 Throw New NotImplementedException() Case Else Throw New ArgumentException("Unknown signature type", "signatureType") End Select End Function ''' <summary> ''' Generate the timestamp for the signature ''' </summary> ''' <returns></returns> Public Overridable 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 ''' <summary> ''' Generate a nonce ''' </summary> ''' <returns></returns> Public Overridable 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