飞越草原

我的草原,我的梦
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Donet读取证书扩展域

Posted on 2006-06-15 15:39  木头's  阅读(627)  评论(0编辑  收藏  举报
//
// server.cs: Web Server that uses ASP.NET hosting
//
// Authors:
//	Gonzalo Paniagua Javier (gonzalo@ximian.com)
//
// (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
// (C) Copyright 2004-2005 Novell, Inc. (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
#if !MODMONO_SERVER
using System.Collections;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Mono.Security.Authenticode;
using Mono.Security.Protocol.Tls;
using MSX = Mono.Security.X509;
using Mono.Security.X509.Extensions;
using SecurityProtocolType = Mono.Security.Protocol.Tls.SecurityProtocolType;
#endif
namespace Mono.XSP {
public class SecurityConfiguration {
#if MODMONO_SERVER
public bool Enabled {
get { return false; }
}
public override string ToString ()
{
return String.Empty;
}
#else
private bool enabled;
private bool valid;
private bool accept_client_certificates;
private bool require_client_certificates;
private RSA key;
private SecurityProtocolType protocol;
private X509Certificate x509;
private string p12Filename;
private string certFilename;
private string keyFilename;
private string password;
public SecurityConfiguration ()
{
protocol = SecurityProtocolType.Default;
}
// properties
public bool AcceptClientCertificates {
get { return accept_client_certificates; }
set { accept_client_certificates = value; }
}
public string CertificateFile {
get { return certFilename; }
set { certFilename = value; }
}
public bool Enabled {
get { return enabled; }
set { enabled = value; }
}
public bool RequireClientCertificates {
get { return require_client_certificates; }
set { require_client_certificates = value; }
}
public string Password {
get { throw new CryptographicException ("Password is write-only."); }
set { password = value; }
}
public string Pkcs12File {
get { return p12Filename; }
set { p12Filename = value; }
}
public RSA KeyPair {
get {
if (!valid)
CheckSecurityContextValidity ();
return key;
}
}
public string PvkFile {
get { return keyFilename; }
set { keyFilename = value; }
}
public X509Certificate ServerCertificate {
get {
if (!valid)
CheckSecurityContextValidity ();
return x509;
}
}
public SecurityProtocolType Protocol {
get { return protocol; }
set { protocol = value; }
}
// methods
public void CheckSecurityContextValidity ()
{
if (enabled) {
// give priority to pkcs12 (for conflicting options)
LoadPkcs12File (p12Filename, password);
LoadCertificate (certFilename);
LoadPrivateKeyFile (keyFilename, password);
}
valid = true;
}
public override string ToString ()
{
if (!enabled)
return "(non-secure)";
StringBuilder sb = new StringBuilder ("(");
switch (protocol) {
case SecurityProtocolType.Default:
sb.Append ("auto-detect SSL3/TLS1");
break;
case SecurityProtocolType.Ssl3:
sb.Append ("SSL3");
break;
case SecurityProtocolType.Tls:
sb.Append ("TLS1");
break;
}
if (require_client_certificates)
sb.Append (" with mandatory client certificates");
sb.Append (")");
return sb.ToString ();
}
public void SetProtocol (string protocol)
{
if (protocol != null) {
try {
this.protocol = (SecurityProtocolType) Enum.Parse (typeof (SecurityProtocolType), protocol);
}
catch (Exception e) {
string message = String.Format ("The value '{0}' given for security protocol is invalid.", protocol);
throw new CryptographicException (message, e);
}
} else {
this.protocol = SecurityProtocolType.Default;
}
}
// private stuff
private void LoadCertificate (string filename)
{
if ((filename == null) || (x509 != null))
return;
try {
x509 = X509Certificate.CreateFromCertFile (filename);
}
catch (Exception e) {
string message = String.Format ("Unable to load X.509 certicate file '{0}'.", filename);
throw new CryptographicException (message, e);
}
}
private void LoadPrivateKeyFile (string filename, string password)
{
if ((filename == null) || (key != null))
return;
try {
if (password == null)
key = PrivateKey.CreateFromFile (filename).RSA;
else
key = PrivateKey.CreateFromFile (filename, password).RSA;
}
catch (CryptographicException ce) {
string message = String.Format ("Invalid private key password or private key file '{0}' is corrupt.", filename);
throw new CryptographicException (message, ce);
}
catch (Exception e) {
string message = String.Format ("Unable to load private key '{0}'.", filename);
throw new CryptographicException (message, e);
}
}
private void LoadPkcs12File (string filename, string password)
{
if ((filename == null) || (key != null))
return;
try {
MSX.PKCS12 pfx = null;
if (password == null)
pfx = MSX.PKCS12.LoadFromFile (filename);
else
pfx = MSX.PKCS12.LoadFromFile (filename, password);
// a PKCS12 file may contain many certificates and keys so we must find
// the best one (i.e. not the first one)
// test for only one certificate / keypair
if (Simple (pfx))
return;
// next, we look for a certificate with the server authentication EKU (oid
// 1.3.6.1.5.5.7.3.1) and, if found, try to find a key associated with it
if (ExtendedKeyUsage (pfx))
return;
// next, we look for a certificate with the KU matching the use of a private
// key matching SSL usage and, if found, try to find a key associated with it
if (KeyUsage (pfx))
return;
// next, we look for the old netscape extension
if (NetscapeCertType (pfx))
return;
// finally we iterate all keys (not certificates) to find a certificate with the
// same public key
if (BruteForce (pfx))
return;
// we don't merit to use SSL ;-)
throw new Exception ("Couldn't find an appropriate certificate and private key to use for SSL.");
}
catch (CryptographicException ce) {
string message = String.Format ("Invalid private key password or private key file '{0}' is corrupt.", filename);
throw new CryptographicException (message, ce);
}
catch (Exception e) {
string message = String.Format ("Unable to load private key '{0}'.", filename);
throw new CryptographicException (message, e);
}
}
private bool Simple (MSX.PKCS12 pfx)
{
int ncerts = pfx.Certificates.Count;
if (ncerts == 0)
throw new Exception ("No certificates are present in the PKCS#12 file.");
int nkeys = pfx.Keys.Count;
if (nkeys == 0)
throw new Exception ("No keypair is present in the PKCS#12 file.");
if (ncerts == 1) {
MSX.X509Certificate cert = pfx.Certificates [0];
// only one certificate, find matching key
if (nkeys == 1) {
// only one key
key = (pfx.Keys [0] as RSA);
} else {
// many keys (strange case)
key = GetKeyMatchingCertificate (pfx, cert);
}
// complete ?
if ((key != null) && (cert != null)) {
x509 = new X509Certificate (cert.RawData);
return true;
}
}
return false;
}
private bool ExtendedKeyUsage (MSX.PKCS12 pfx)
{
foreach (MSX.X509Certificate cert in pfx.Certificates) {
MSX.X509Extension xtn = cert.Extensions ["2.5.29.37"];
if (xtn == null)
continue;
ExtendedKeyUsageExtension eku = new ExtendedKeyUsageExtension (xtn);
if (!eku.KeyPurpose.Contains ("1.3.6.1.5.5.7.3.1"))
continue;
key = GetKeyMatchingCertificate (pfx, cert);
if (key == null)
continue;
x509 = new X509Certificate (cert.RawData);
break;
}
// complete ?
return ((x509 != null) && (key != null));
}
private bool KeyUsage (MSX.PKCS12 pfx)
{
foreach (MSX.X509Certificate cert in pfx.Certificates) {
MSX.X509Extension xtn = cert.Extensions ["2.5.29.15"];
if (xtn == null)
continue;
KeyUsageExtension ku = new KeyUsageExtension (xtn);
if (!ku.Support (KeyUsages.digitalSignature) && !ku.Support (KeyUsages.keyEncipherment))
continue;
key = GetKeyMatchingCertificate (pfx, cert);
if (key == null)
continue;
x509 = new X509Certificate (cert.RawData);
break;
}
// complete ?
return ((x509 != null) && (key != null));
}
private bool NetscapeCertType (MSX.PKCS12 pfx)
{
foreach (MSX.X509Certificate cert in pfx.Certificates) {
MSX.X509Extension xtn = cert.Extensions ["2.16.840.1.113730.1.1"];
if (xtn == null)
continue;
NetscapeCertTypeExtension ct = new NetscapeCertTypeExtension (xtn);
if (!ct.Support (NetscapeCertTypeExtension.CertTypes.SslServer))
continue;
key = GetKeyMatchingCertificate (pfx, cert);
if (key == null)
continue;
x509 = new X509Certificate (cert.RawData);
break;
}
// complete ?
return ((x509 != null) && (key != null));
}
private bool BruteForce (MSX.PKCS12 pfx)
{
foreach (object o in pfx.Keys) {
key = (o as RSA);
if (key == null)
continue;
string s = key.ToXmlString (false);
foreach (MSX.X509Certificate cert in pfx.Certificates) {
if (s == cert.RSA.ToXmlString (false))
x509 = new X509Certificate (cert.RawData);
}
// complete ?
if ((x509 != null) && (key != null))
return true;
}
return false;
}
private RSA GetKeyMatchingCertificate (MSX.PKCS12 pfx, MSX.X509Certificate cert)
{
IDictionary attributes = pfx.GetAttributes (cert);
return (pfx.GetAsymmetricAlgorithm (attributes) as RSA);
}
#endif
}
}