//----------------------------------------------------------------------- // // Copyright (c) Andrew Arnott. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth.ChannelElements { using System; using System.Diagnostics.Contracts; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using DotNetOpenAuth.Messaging; /// /// A binding element that signs outgoing messages and verifies the signature on incoming messages. /// public class RsaSha1SigningBindingElement : SigningBindingElementBase { /// /// The name of the hash algorithm to use. /// private const string HashAlgorithmName = "RSA-SHA1"; /// /// The token manager for the service provider. /// private IServiceProviderTokenManager tokenManager; /// /// Initializes a new instance of the class /// for use by Consumers. /// /// The certificate used to sign outgoing messages. public RsaSha1SigningBindingElement(X509Certificate2 signingCertificate) : base(HashAlgorithmName) { Contract.Requires(signingCertificate != null); this.SigningCertificate = signingCertificate; } /// /// Initializes a new instance of the class /// for use by Service Providers. /// /// The token manager. public RsaSha1SigningBindingElement(IServiceProviderTokenManager tokenManager) : base(HashAlgorithmName) { Contract.Requires(tokenManager != null); this.tokenManager = tokenManager; } /// /// Gets or sets the certificate used to sign outgoing messages. Used only by Consumers. /// public X509Certificate2 SigningCertificate { get; set; } /// /// Calculates a signature for a given message. /// /// The message to sign. /// The signature for the message. /// /// This method signs the message per OAuth 1.0 section 9.3. /// protected override string GetSignature(ITamperResistantOAuthMessage message) { ErrorUtilities.VerifyOperation(this.SigningCertificate != null, OAuthStrings.X509CertificateNotProvidedForSigning); string signatureBaseString = ConstructSignatureBaseString(message, this.Channel.MessageDescriptions.GetAccessor(message)); byte[] data = Encoding.ASCII.GetBytes(signatureBaseString); var provider = (RSACryptoServiceProvider)this.SigningCertificate.PrivateKey; byte[] binarySignature = provider.SignData(data, "SHA1"); string base64Signature = Convert.ToBase64String(binarySignature); return base64Signature; } /// /// Determines whether the signature on some message is valid. /// /// The message to check the signature on. /// /// true if the signature on the message is valid; otherwise, false. /// protected override bool IsSignatureValid(ITamperResistantOAuthMessage message) { ErrorUtilities.VerifyInternal(this.tokenManager != null, "No token manager available for fetching Consumer public certificates."); string signatureBaseString = ConstructSignatureBaseString(message, this.Channel.MessageDescriptions.GetAccessor(message)); byte[] data = Encoding.ASCII.GetBytes(signatureBaseString); byte[] carriedSignature = Convert.FromBase64String(message.Signature); X509Certificate2 cert = this.tokenManager.GetConsumer(message.ConsumerKey).Certificate; if (cert == null) { Logger.Signatures.WarnFormat("Incoming message from consumer '{0}' could not be matched with an appropriate X.509 certificate for signature verification.", message.ConsumerKey); return false; } var provider = (RSACryptoServiceProvider)cert.PublicKey.Key; bool valid = provider.VerifyData(data, "SHA1", carriedSignature); return valid; } /// /// Clones this instance. /// /// A new instance of the binding element. protected override ITamperProtectionChannelBindingElement Clone() { if (this.tokenManager != null) { return new RsaSha1SigningBindingElement(this.tokenManager); } else { return new RsaSha1SigningBindingElement(this.SigningCertificate); } } } }