/// <summary> /// Computes the hash of the StringToSign using the secret key. /// </summary> /// <param name="stringToSign">The StringToSign that will be hashed with the secret key.</param> /// <param name="authMethod">The authorization/hash method to be used.</param> /// <returns>The hash of the string to sign.</returns> public string ComputeHash( string stringToSign, AuthenticationMethod authMethod) { CheckArgument.NotNullOrEmpty(stringToSign, "stringToSign"); byte[] secretKeyBytes = Convert.FromBase64String(this.credential.SecretKey); byte[] stringToSignBytes = Encoding.UTF8.GetBytes(stringToSign); HMAC hasher = null; switch (authMethod) { case AuthenticationMethod.HmacSha1: hasher = new HMACSHA1(secretKeyBytes); break; case AuthenticationMethod.HmacSha256: hasher = new HMACSHA256(secretKeyBytes); break; default: throw new NotSupportedException("Only HMAC SHA1 and SHA256 authentication is currently supported"); } return(Convert.ToBase64String(hasher.ComputeHash(stringToSignBytes))); }
/// <summary> /// Constructs the "StringToSign" as defined in the TeleSign REST API. This is the string that is hashed /// and that hash is used to authenticate the user. Then the authorization string is created /// by from the customer id and the StringToSign hashed with the secret key. /// </summary> /// <param name="resourceName">The name of the resource - ie. the relative part of the URL.</param> /// <param name="method">The http method - POST, DELETE, GET, PUT.</param> /// <param name="timestamp">The timestamp to use.</param> /// <param name="nonce">The nonce (used for preventing replay attacks).</param> /// <param name="contentType">The mime type content type.</param> /// <param name="encodedBody">The body of a POST request.</param> /// <param name="authMethod">The authentication method to use.</param> /// <returns>The string that will be signed for authentication.</returns> public string ConstructStringToSign( string resourceName, string method, DateTime timestamp, string nonce, string contentType, string encodedBody, AuthenticationMethod authMethod) { CheckArgument.NotNullOrEmpty(resourceName, "resourceName"); CheckArgument.NotNullOrEmpty(method, "method"); CheckArgument.NotNull(nonce, "nonce"); CheckArgument.NotNull(contentType, "contentType"); CheckArgument.NotNull(encodedBody, "encodedBody"); string authMethodString = this.MapAuthenticationMethodToHeaderString(authMethod); return(string.Format( CultureInfo.InvariantCulture, "{0}\n{1}\n\nx-ts-auth-method:{2}\nx-ts-date:{3}\nx-ts-nonce:{4}\n{5}{6}", method, contentType, authMethodString, timestamp.ToString("r"), nonce, encodedBody.Length == 0 ? string.Empty : encodedBody + "\n", resourceName)); }
/// <summary> /// Cleans up phone number strings by removing any non-digit characters from /// them. Will throw an exception if the resulting cleaned up string has /// no characters. /// </summary> /// <param name="phoneNumber">The input phone number.</param> /// <returns>The input phone number with all non-digit characters removed.</returns> public string CleanupPhoneNumber(string phoneNumber) { CheckArgument.NotNullOrEmpty(phoneNumber, "phoneNumber"); StringBuilder builder = new StringBuilder( phoneNumber.Length, phoneNumber.Length); // Remove non-digit characters from the phone number. foreach (char c in phoneNumber) { if (Char.IsDigit(c)) { builder.Append(c); } } // Reject empty strings. if (builder.Length == 0) { string message = string.Format( CultureInfo.InvariantCulture, "Phone Number '{0}' contains no digits", phoneNumber); throw new ArgumentException( message, "phoneNumber"); } return(builder.ToString()); }
/// <summary> /// Initializes a new instance of the TeleSignCredential class. /// </summary> /// <param name="customerId">The customer id.</param> /// <param name="secretKey">The customers secret key.</param> public TeleSignCredential( Guid customerId, string secretKey) { CheckArgument.NotNullOrEmpty(secretKey, "secretKey"); this.CustomerId = customerId; this.SecretKey = secretKey; }
/// <summary> /// Constructs the authorization by combining the customer id /// from the credential with the stringToSign hashed with /// the secret key. /// </summary> /// <param name="stringToSign"> /// The stringToSign that is used for authentication. /// </param> /// <param name="authMethod">The method used for authentication.</param> /// <returns>The authorization string.</returns> public string ConstructAuthorizationString( string stringToSign, AuthenticationMethod authMethod) { CheckArgument.NotNullOrEmpty(stringToSign, "stringToSign"); return(string.Format( CultureInfo.InvariantCulture, "TSA {0}:{1}", this.credential.CustomerId, this.ComputeHash(stringToSign, authMethod))); }
/// <summary> /// Validates that the array is not null and of at least a certain length. /// If the array is null an ArgumentNullException is throw per the NotNull /// method - if the array is shorter than the required length an /// ArgumentException is thrown. Otherwise does nothing. /// </summary> /// <param name="value">The array to check.</param> /// <param name="minimumLength">The minimum length the array should be.</param> /// <param name="parameterName">The name of the parameter. Used to populate the exception.</param> public static void ArrayLengthAtLeast( Array value, int minimumLength, string parameterName) { CheckArgument.NotNull(value, parameterName); if (value.Length < minimumLength) { string message = string.Format( CultureInfo.InvariantCulture, "Array '{0}' has length '{1}' but must be at least '{2}'", parameterName, value.Length, minimumLength); throw new ArgumentException( message, FormatParameterName(parameterName)); } }
/// <summary> /// Constructs the "StringToSign" as defined in the TeleSign REST API. This is the string that is hashed /// and that hash is used to authenticate the user. Then the authorization string is created /// by from the customer id and the StringToSign hashed with the secret key. /// </summary> /// <param name="resourceName">The name of the resource - ie. the relative part of the URL.</param> /// <param name="method">The http method - POST, DELETE, GET, PUT.</param> /// <param name="contentType">The mime type content type.</param> /// <param name="encodedBody">The body of a POST request.</param> /// <returns>The string that will be signed for authentication.</returns> public string ConstructStringToSign( string resourceName, string method, string contentType, string encodedBody) { CheckArgument.NotNullOrEmpty(resourceName, "resourceName"); CheckArgument.NotNullOrEmpty(method, "method"); CheckArgument.NotNull(contentType, "contentType"); CheckArgument.NotNull(encodedBody, "encodedBody"); DateTime timestamp = DateTime.UtcNow; string nonce = Guid.NewGuid().ToString(); return(this.ConstructStringToSign( resourceName, method, timestamp, nonce, contentType, encodedBody, AuthenticationMethod.HmacSha1)); }
/// <summary> /// Initializes a new instance of the TeleSignAuthentication class with /// a credential. /// </summary> /// <param name="credential"> /// The TeleSign credentials for authenticating with the TeleSign services. /// </param> public TeleSignAuthentication(TeleSignCredential credential) { CheckArgument.NotNull(credential, "credential"); this.credential = credential; }
/// <summary> /// Constructs a .NET WebRequest object for the request (using for Mobile). /// </summary> /// <param name="resourceName">The name of the resource - ie. the relative part of the URL.</param> /// <param name="method">The http method - POST, DELETE, GET, PUT.</param> /// <param name="fields">The fields that are the arguments to the request.</param> /// <param name="authMethod">The method of authentication to use.</param> /// <returns>A WebRequest object.</returns> protected WebRequest ConstructWebMobileRequest( string resourceName, string method, Dictionary <string, string> fields = null, AuthenticationMethod authMethod = AuthenticationMethod.HmacSha1) { CheckArgument.NotNullOrEmpty(resourceName, "resourceName"); CheckArgument.NotNullOrEmpty(method, "method"); DateTime timeStamp = DateTime.UtcNow; string nonce = Guid.NewGuid().ToString(); // When the Uri is constructed. If it is a GET request the fields // are put into the Uri's query string eg ?foo=bar. When the // method is POST the fields are not used in constructing the Uri, // but below they are placed in the encoded body. Uri fullUri = this.ConstructResourceMobileUri( resourceName, method, fields); HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(fullUri); request.Method = method; string contentType = string.Empty; string encodedBody = string.Empty; if (method == "POST") { contentType = "application/x-www-form-urlencoded"; encodedBody = TeleSignService.ConstructQueryString(fields); } string authorizationString = this.authentication.ConstructAuthorizationString( resourceName, method, timeStamp, nonce, contentType, encodedBody, authMethod); request.Headers.Add("Authorization", authorizationString); request.Headers.Add("x-ts-auth-method", this.authentication.MapAuthenticationMethodToHeaderString(authMethod)); request.Headers.Add("x-ts-date", timeStamp.ToString("r")); request.Headers.Add("x-ts-nonce", nonce); if (method == "POST") { byte[] body = Encoding.UTF8.GetBytes(encodedBody); request.Accept = "application/json"; request.ContentType = contentType; request.ContentLength = body.Length; using (Stream stream = request.GetRequestStream()) { stream.Write(body, 0, body.Length); } } return(request); }
/// <summary> /// Checks if the provided object is null. If it is an ArgumentNullException /// is throw otherwise it does nothing. This is just a way to simplify/shorten /// parameter checkin. /// </summary> /// <param name="value">The value to check for null.</param> /// <param name="parameterName"> /// The name of the parameter. This is used to populate the exception. /// </param> public static void NotNullOrEmpty(string value, string parameterName) { CheckArgument.NotNull(value, parameterName); CheckArgument.NotEmpty(value, parameterName); }