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