2009-05-21 24 views
9

Aparentemente estaba haciendo la pregunta incorrecta en mi publicación anterior. Tengo un servicio web asegurado con un certificado X.509, que se ejecuta como un sitio web seguro (https://..). Deseo utilizar el certificado de máquina del cliente (también X.509) emitido por la CA raíz de la compañía para verificar al servidor que la máquina cliente está autorizada para usar el servicio. Para hacer esto, necesito inspeccionar el certificado y buscar alguna característica de identificación que coincida con un valor almacenado en una base de datos (¿tal vez la Huella digital?).¿Cómo obtengo el Certificado X509 enviado desde el cliente en el servicio web?

Este es el código que utilizo para obtener el certificado del almacén de certificados local (levantado directamente de http://msdn.microsoft.com/en-us/magazine/cc163454.aspx):

public static class SecurityCertificate 
{ 
    private static X509Certificate2 _certificate = null; 

    public static X509Certificate2 Certificate 
    { 
     get { return _certificate; } 
    } 

    public static bool LoadCertificate() 
    { 
     // get thumbprint from app.config 
     string thumbPrint = Properties.Settings.Default.Thumbprint; 
     if (string.IsNullOrEmpty(thumbPrint)) 
     { 
      // if no thumbprint on file, user must select certificate to use 
      _certificate = PickCertificate(StoreLocation.LocalMachine, StoreName.My); 
      if (null != _certificate) 
      { 
       // show certificate details dialog 
       X509Certificate2UI.DisplayCertificate(_certificate); 
       Properties.Settings.Default.Thumbprint = _certificate.Thumbprint; 
       Properties.Settings.Default.Save(); 
      } 
     } 
     else 
     { 
      _certificate = FindCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, thumbPrint); 
     } 

     if (null == _certificate) 
     { 
      MessageBox.Show("You must have a valid machine certificate to use STS."); 
      return false; 
     } 

     return true; 
    } 

    private static X509Certificate2 PickCertificate(StoreLocation location, StoreName name) 
    { 
     X509Store store = new X509Store(name, location); 
     try 
     { 
      // create and open store for read-only access 
      store.Open(OpenFlags.ReadOnly); 

      X509Certificate2Collection coll = store.Certificates.Find(X509FindType.FindByIssuerName, STSClientConstants.NBCCA, true); 
      if (0 == coll.Count) 
      { 
       MessageBox.Show("No valid machine certificate found - please contact tech support."); 
       return null; 
      } 

      // pick a certificate from the store 
      coll = null; 
      while (null == coll || 0 == coll.Count) 
      { 
       coll = X509Certificate2UI.SelectFromCollection(
         store.Certificates, "Local Machine Certificates", 
         "Select one", X509SelectionFlag.SingleSelection); 
      } 

      // return first certificate found 
      return coll[ 0 ]; 
     } 
     // always close the store 
     finally { store.Close(); } 
    } 

    private static X509Certificate2 FindCertificate(StoreLocation location, StoreName name, X509FindType findType, string findValue) 
    { 
     X509Store store = new X509Store(name, location); 
     try 
     { 
      // create and open store for read-only access 
      store.Open(OpenFlags.ReadOnly); 

      // search store 
      X509Certificate2Collection col = store.Certificates.Find(findType, findValue, true); 

      // return first certificate found 
      return col[ 0 ]; 
     } 
     // always close the store 
     finally { store.Close(); } 
    } 

Entonces, hay que adjuntar el certificado a la corriente de salida de esta manera:

public static class ServiceDataAccess 
{  
    private static STSWebService _stsWebService = new STSWebService(); 

    public static DataSet GetData(Dictionary<string,string> DictParam, string action) 
    { 
     // add the machine certificate here, the first web service call made by the program (only called once) 
     _stsWebService.ClientCertificates.Add(SecurityCertificate.Certificate); 
     // rest of web service call here... 
    } 
} 

Mi pregunta es esta: ¿cómo puedo "obtener" el certificado en el código del servicio web? La mayoría de los ejemplos de fragmentos de código con los que me he encontrado que cubren cómo hacer la validación personalizada tienen una llamada GetCertificate() ahí, aparentemente asumiendo que esa parte es tan fácil que todos deberían saber cómo hacerlo.

Mi clase principal hereda de WebService, entonces puedo usar Context.Request.ClientCertificate para obtener un certificado, pero eso es un HttpClientCertificate, no un X509Certificate2. HttpContext me da el mismo resultado. Otros enfoques utilizan el código de configuración web para llamar al código de verificación predefinido, sin ninguna pista sobre cómo llamar a un método C# personalizado para realizar la verificación.

Estoy seguro de que esta es otra "pregunta estúpida" que alguien volverá y dirá "mira, haz esto y funciona", pero está bien. He pasado muchas horas tratando de hacer que esto funcione, y mi orgullo es casi inexistente en este momento. ¿Puede alguien mostrarme el error de mis caminos?

Gracias, de Dave

+0

Si no tiene HttpContext, consulte la respuesta para http: // stackoverflow.com/questions/7528455/how-to-get-the-x509certificate-from-a-client-request? lq = 1 –

Respuesta

12

Recuerdo hacer algo similar, su sido un tiempo, pero, ¿ha intentado esto en su servicio web:

X509Certificate2 cert = new X509Certificate2(Context.Request.ClientCertificate.Certificate); 
+0

¡Eso funciona! Ahora puedo finalmente llegar a la cumbre de mi problema: averiguar qué valor puedo usar para que coincida con un valor almacenado en la base de datos ... ¡Gracias! – DaveN59

+1

Thumbprint está bien para usar si está tratando su base de datos como la autoridad de confianza, he trabajado en aplicaciones que tuvieron este enfoque. OTOH, si necesita un modelo de confianza distribuida (usando CA), entonces necesita hacer comprobaciones adicionales (por ejemplo, verificar la cadena de certificados). – DSO

+0

Creo que puedo haber actuado demasiado pronto. Intentar acceder a la huella digital arroja una excepción 'System.Security.Cryptography.CryptographicException'; por lo tanto, aunque ahora el certificado http es del tipo X509Certificate2, ¿es realmente el mismo certificado enviado por el cliente, o simplemente creé uno nuevo? bestia que solo se parece a eso? – DaveN59

1

Sobre el tema de cómo volver a vincular el certificado a un usuario, suponiendo que la identidad del usuario asociado con la clave es buena (ya que el certificado se ha verificado de nuevo en una raíz de confianza y no tiene b una vez revocado), entonces debe vincular la identidad reclamada por el certificado a un usuario. Puede usar el formulario de cadena LDAP del DN del sujeto y buscarlo (cn = Nombre de usuario, ou = Departamento ...) para determinar el ID local. Esto es resiliente en el caso de que el usuario vuelva a generar su clave/certificado, por ejemplo debido a una pérdida de tarjeta o expiración natural del certificado. Esto se basa en el hecho de que dos CA no emitirán dos certs con el mismo nombre de sujeto para dos personas diferentes.

Si el certificado fue emitido por una MS CA podría tener un UPN que efectivamente es un nombre de inicio de sesión de dominio.

Alternativamente, si desea vincular la identidad del usuario a un certificado real, el método habitual es almacenar el nombre del emisor y el número de serie del certificado. Las CA deben emitir números de serie únicos para cada certificado. Tenga en cuenta que los números de serie pueden ser grandes dependiendo de la CA. Sin embargo, el uso de este método significa que los detalles del certificado en la base de datos se deben actualizar cada vez que se vuelva a emitir el certificado del usuario.

+1

¡Excelente información! Si todavía estuviera trabajando en el proyecto, este es exactamente el tipo de información que necesitaría para solucionar los problemas que he creado en la primera implementación. Sin embargo, dado que ahora me he mudado a una empresa diferente con responsabilidades diferentes, lo dejaré como ejercicio para otra persona ... ¡Gracias por los comentarios! – DaveN59

Cuestiones relacionadas