|
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
//
// X509Certificate2Collection.cs
//
namespace System.Security.Cryptography.X509Certificates {
using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Runtime.Versioning;
using _FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
public enum X509FindType {
FindByThumbprint = 0,
FindBySubjectName = 1,
FindBySubjectDistinguishedName = 2,
FindByIssuerName = 3,
FindByIssuerDistinguishedName = 4,
FindBySerialNumber = 5,
FindByTimeValid = 6,
FindByTimeNotYetValid = 7,
FindByTimeExpired = 8,
FindByTemplateName = 9,
FindByApplicationPolicy = 10,
FindByCertificatePolicy = 11,
FindByExtension = 12,
FindByKeyUsage = 13,
FindBySubjectKeyIdentifier = 14
}
public class X509Certificate2Collection : X509CertificateCollection {
public X509Certificate2Collection() {}
public X509Certificate2Collection(X509Certificate2 certificate) {
this.Add(certificate);
}
public X509Certificate2Collection(X509Certificate2Collection certificates) {
this.AddRange(certificates);
}
public X509Certificate2Collection(X509Certificate2[] certificates) {
this.AddRange(certificates);
}
public new X509Certificate2 this[int index] {
get {
return (X509Certificate2) List[index];
}
set {
if (value == null)
throw new ArgumentNullException("value");
List[index] = value;
}
}
public int Add(X509Certificate2 certificate) {
if (certificate == null)
throw new ArgumentNullException("certificate");
return List.Add(certificate);
}
public void AddRange(X509Certificate2[] certificates) {
if (certificates == null)
throw new ArgumentNullException("certificates");
int i=0;
try {
for (; i<certificates.Length; i++) {
Add(certificates[i]);
}
} catch {
for (int j=0; j<i; j++) {
Remove(certificates[j]);
}
throw;
}
}
public void AddRange(X509Certificate2Collection certificates) {
if (certificates == null)
throw new ArgumentNullException("certificates");
int i = 0;
try {
foreach (X509Certificate2 certificate in certificates) {
Add(certificate);
i++;
}
} catch {
for (int j=0; j<i; j++) {
Remove(certificates[j]);
}
throw;
}
}
public bool Contains(X509Certificate2 certificate) {
if (certificate == null)
throw new ArgumentNullException("certificate");
return List.Contains(certificate);
}
public void Insert(int index, X509Certificate2 certificate) {
if (certificate == null)
throw new ArgumentNullException("certificate");
List.Insert(index, certificate);
}
public new X509Certificate2Enumerator GetEnumerator() {
return new X509Certificate2Enumerator(this);
}
public void Remove(X509Certificate2 certificate) {
if (certificate == null)
throw new ArgumentNullException("certificate");
List.Remove(certificate);
}
public void RemoveRange(X509Certificate2[] certificates) {
if (certificates == null)
throw new ArgumentNullException("certificates");
int i=0;
try {
for (; i<certificates.Length; i++) {
Remove(certificates[i]);
}
} catch {
for (int j=0; j<i; j++) {
Add(certificates[j]);
}
throw;
}
}
public void RemoveRange(X509Certificate2Collection certificates) {
if (certificates == null)
throw new ArgumentNullException("certificates");
int i = 0;
try {
foreach (X509Certificate2 certificate in certificates) {
Remove(certificate);
i++;
}
} catch {
for (int j=0; j<i; j++) {
Add(certificates[j]);
}
throw;
}
}
#if FEATURE_CORESYSTEM
[SecuritySafeCritical]
#endif
public X509Certificate2Collection Find(X509FindType findType, Object findValue, bool validOnly) {
#if !FEATURE_CORESYSTEM
//
// We need to Assert all StorePermission flags since this is a memory store and we want
// semi-trusted code to be able to find certificates in a memory store.
//
StorePermission sp = new StorePermission(StorePermissionFlags.AllFlags);
sp.Assert();
#endif
SafeCertStoreHandle safeSourceStoreHandle = X509Utils.ExportToMemoryStore(this);
SafeCertStoreHandle safeTargetStoreHandle = FindCertInStore(safeSourceStoreHandle, findType, findValue, validOnly);
X509Certificate2Collection collection = X509Utils.GetCertificates(safeTargetStoreHandle);
safeTargetStoreHandle.Dispose();
safeSourceStoreHandle.Dispose();
return collection;
}
public void Import(byte[] rawData) {
Import(rawData, null, X509KeyStorageFlags.DefaultKeySet);
}
#if FEATURE_CORESYSTEM
[SecuritySafeCritical]
#endif
public void Import(byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags) {
uint dwFlags = X509Utils.MapKeyStorageFlags(keyStorageFlags);
SafeCertStoreHandle safeCertStoreHandle = SafeCertStoreHandle.InvalidHandle;
#if !FEATURE_CORESYSTEM
//
// We need to Assert all StorePermission flags since this is a memory store and we want
// semi-trusted code to be able to import certificates to a memory store.
//
StorePermission sp = new StorePermission(StorePermissionFlags.AllFlags);
sp.Assert();
#endif
safeCertStoreHandle = LoadStoreFromBlob(rawData, password, dwFlags, (keyStorageFlags & X509KeyStorageFlags.PersistKeySet) != 0);
X509Certificate2Collection collection = X509Utils.GetCertificates(safeCertStoreHandle);
safeCertStoreHandle.Dispose();
X509Certificate2[] x509Certs = new X509Certificate2[collection.Count];
collection.CopyTo(x509Certs, 0);
this.AddRange(x509Certs);
}
[ResourceExposure(ResourceScope.Machine)]
#if !FEATURE_CORESYSTEM
[ResourceConsumption(ResourceScope.Machine)]
#endif
public void Import(string fileName) {
Import(fileName, null, X509KeyStorageFlags.DefaultKeySet);
}
#if FEATURE_CORESYSTEM
[SecuritySafeCritical]
#endif
[ResourceExposure(ResourceScope.Machine)]
#if !FEATURE_CORESYSTEM
[ResourceConsumption(ResourceScope.Machine)]
#endif
public void Import(string fileName, string password, X509KeyStorageFlags keyStorageFlags) {
uint dwFlags = X509Utils.MapKeyStorageFlags(keyStorageFlags);
SafeCertStoreHandle safeCertStoreHandle = SafeCertStoreHandle.InvalidHandle;
#if !FEATURE_CORESYSTEM
//
// We need to Assert all StorePermission flags since this is a memory store and we want
// semi-trusted code to be able to import certificates to a memory store.
//
StorePermission sp = new StorePermission(StorePermissionFlags.AllFlags);
sp.Assert();
#endif
safeCertStoreHandle = LoadStoreFromFile(fileName, password, dwFlags, (keyStorageFlags & X509KeyStorageFlags.PersistKeySet) != 0);
X509Certificate2Collection collection = X509Utils.GetCertificates(safeCertStoreHandle);
safeCertStoreHandle.Dispose();
X509Certificate2[] x509Certs = new X509Certificate2[collection.Count];
collection.CopyTo(x509Certs, 0);
this.AddRange(x509Certs);
}
#if FEATURE_CORESYSTEM
[SecuritySafeCritical]
#endif
public byte[] Export(X509ContentType contentType) {
return Export(contentType, null);
}
#if FEATURE_CORESYSTEM
[SecuritySafeCritical]
#endif
public byte[] Export(X509ContentType contentType, string password) {
#if !FEATURE_CORESYSTEM
//
// We need to Assert all StorePermission flags since this is a memory store and we want
// semi-trusted code to be able to export certificates to a memory store.
//
StorePermission sp = new StorePermission(StorePermissionFlags.AllFlags);
sp.Assert();
#endif
SafeCertStoreHandle safeCertStoreHandle = X509Utils.ExportToMemoryStore(this);
byte[] result = ExportCertificatesToBlob(safeCertStoreHandle, contentType, password);
safeCertStoreHandle.Dispose();
return result;
}
#if FEATURE_CORESYSTEM
[SecurityCritical]
#endif
private unsafe static byte[] ExportCertificatesToBlob(SafeCertStoreHandle safeCertStoreHandle, X509ContentType contentType, string password) {
SafeCertContextHandle safeCertContextHandle = SafeCertContextHandle.InvalidHandle;
uint dwSaveAs = CAPI.CERT_STORE_SAVE_AS_PKCS7;
byte[] pbBlob = null;
CAPI.CRYPTOAPI_BLOB DataBlob = new CAPI.CRYPTOAPI_BLOB();
SafeLocalAllocHandle pbEncoded = SafeLocalAllocHandle.InvalidHandle;
switch(contentType) {
case X509ContentType.Cert:
safeCertContextHandle = CAPI.CertEnumCertificatesInStore(safeCertStoreHandle, safeCertContextHandle);
if (safeCertContextHandle != null && !safeCertContextHandle.IsInvalid) {
CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle());
pbBlob = new byte[pCertContext.cbCertEncoded];
Marshal.Copy(pCertContext.pbCertEncoded, pbBlob, 0, pbBlob.Length);
}
break;
case X509ContentType.SerializedCert:
safeCertContextHandle = CAPI.CertEnumCertificatesInStore(safeCertStoreHandle, safeCertContextHandle);
uint cbEncoded = 0;
if (safeCertContextHandle != null && !safeCertContextHandle.IsInvalid) {
if (!CAPI.CertSerializeCertificateStoreElement(safeCertContextHandle,
0,
pbEncoded,
new IntPtr(&cbEncoded)))
throw new CryptographicException(Marshal.GetLastWin32Error());
pbEncoded = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(cbEncoded));
if (!CAPI.CertSerializeCertificateStoreElement(safeCertContextHandle,
0,
pbEncoded,
new IntPtr(&cbEncoded)))
throw new CryptographicException(Marshal.GetLastWin32Error());
pbBlob = new byte[cbEncoded];
Marshal.Copy(pbEncoded.DangerousGetHandle(), pbBlob, 0, pbBlob.Length);
}
break;
case X509ContentType.Pkcs12:
if (!CAPI.PFXExportCertStore(safeCertStoreHandle,
new IntPtr(&DataBlob),
password,
CAPI.EXPORT_PRIVATE_KEYS | CAPI.REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY))
throw new CryptographicException(Marshal.GetLastWin32Error());
pbEncoded = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(DataBlob.cbData));
DataBlob.pbData = pbEncoded.DangerousGetHandle();
if (!CAPI.PFXExportCertStore(safeCertStoreHandle,
new IntPtr(&DataBlob),
password,
CAPI.EXPORT_PRIVATE_KEYS | CAPI.REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY))
throw new CryptographicException(Marshal.GetLastWin32Error());
pbBlob = new byte[DataBlob.cbData];
Marshal.Copy(DataBlob.pbData, pbBlob, 0, pbBlob.Length);
break;
case X509ContentType.SerializedStore:
// falling through
case X509ContentType.Pkcs7:
if (contentType == X509ContentType.SerializedStore)
dwSaveAs = CAPI.CERT_STORE_SAVE_AS_STORE;
// determine the required length
if (!CAPI.CertSaveStore(safeCertStoreHandle,
CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING,
dwSaveAs,
CAPI.CERT_STORE_SAVE_TO_MEMORY,
new IntPtr(&DataBlob),
0))
throw new CryptographicException(Marshal.GetLastWin32Error());
pbEncoded = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(DataBlob.cbData));
DataBlob.pbData = pbEncoded.DangerousGetHandle();
// now save the store to a memory blob
if (!CAPI.CertSaveStore(safeCertStoreHandle,
CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING,
dwSaveAs,
CAPI.CERT_STORE_SAVE_TO_MEMORY,
new IntPtr(&DataBlob),
0))
throw new CryptographicException(Marshal.GetLastWin32Error());
pbBlob = new byte[DataBlob.cbData];
Marshal.Copy(DataBlob.pbData, pbBlob, 0, pbBlob.Length);
break;
default:
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidContentType));
}
pbEncoded.Dispose();
safeCertContextHandle.Dispose();
return pbBlob;
}
#if FEATURE_CORESYSTEM
[SecurityCritical]
#endif
internal delegate int FindProcDelegate (SafeCertContextHandle safeCertContextHandle, object pvCallbackData);
#if FEATURE_CORESYSTEM
[SecuritySafeCritical]
#endif
private unsafe static SafeCertStoreHandle FindCertInStore(SafeCertStoreHandle safeSourceStoreHandle, X509FindType findType, Object findValue, bool validOnly) {
if (findValue == null)
throw new ArgumentNullException("findValue");
IntPtr pvFindPara = IntPtr.Zero;
object pvCallbackData1 = null;
object pvCallbackData2 = null;
FindProcDelegate pfnCertCallback1 = null;
FindProcDelegate pfnCertCallback2 = null;
uint dwFindType = CAPI.CERT_FIND_ANY;
string subject, issuer;
CAPI.CRYPTOAPI_BLOB HashBlob = new CAPI.CRYPTOAPI_BLOB();
SafeLocalAllocHandle pb = SafeLocalAllocHandle.InvalidHandle;
_FILETIME ft = new _FILETIME();
string oidValue = null;
switch(findType) {
case X509FindType.FindByThumbprint:
if (findValue.GetType() != typeof(string))
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindValue));
byte[] hex = X509Utils.DecodeHexString((string) findValue);
pb = X509Utils.ByteToPtr(hex);
HashBlob.pbData = pb.DangerousGetHandle();
HashBlob.cbData = (uint) hex.Length;
dwFindType = CAPI.CERT_FIND_HASH;
pvFindPara = new IntPtr(&HashBlob);
break;
case X509FindType.FindBySubjectName:
if (findValue.GetType() != typeof(string))
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindValue));
subject = (string) findValue;
dwFindType = CAPI.CERT_FIND_SUBJECT_STR;
pb = X509Utils.StringToUniPtr(subject);
pvFindPara = pb.DangerousGetHandle();
break;
case X509FindType.FindBySubjectDistinguishedName:
if (findValue.GetType() != typeof(string))
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindValue));
subject = (string) findValue;
pfnCertCallback1 = new FindProcDelegate(FindSubjectDistinguishedNameCallback);
pvCallbackData1 = subject;
break;
case X509FindType.FindByIssuerName:
if (findValue.GetType() != typeof(string))
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindValue));
issuer = (string) findValue;
dwFindType = CAPI.CERT_FIND_ISSUER_STR;
pb = X509Utils.StringToUniPtr(issuer);
pvFindPara = pb.DangerousGetHandle();
break;
case X509FindType.FindByIssuerDistinguishedName:
if (findValue.GetType() != typeof(string))
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindValue));
issuer = (string) findValue;
pfnCertCallback1 = new FindProcDelegate(FindIssuerDistinguishedNameCallback);
pvCallbackData1 = issuer;
break;
case X509FindType.FindBySerialNumber:
if (findValue.GetType() != typeof(string))
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindValue));
pfnCertCallback1 = new FindProcDelegate(FindSerialNumberCallback);
pfnCertCallback2 = new FindProcDelegate(FindSerialNumberCallback);
BigInt h = new BigInt();
h.FromHexadecimal((string) findValue);
pvCallbackData1 = (byte[]) h.ToByteArray();
h.FromDecimal((string) findValue);
pvCallbackData2 = (byte[]) h.ToByteArray();
break;
case X509FindType.FindByTimeValid:
if (findValue.GetType() != typeof(DateTime))
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindValue));
*((long*) &ft) = ((DateTime) findValue).ToFileTime();
pfnCertCallback1 = new FindProcDelegate(FindTimeValidCallback);
pvCallbackData1 = ft;
break;
case X509FindType.FindByTimeNotYetValid:
if (findValue.GetType() != typeof(DateTime))
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindValue));
*((long*) &ft) = ((DateTime) findValue).ToFileTime();
pfnCertCallback1 = new FindProcDelegate(FindTimeNotBeforeCallback);
pvCallbackData1 = ft;
break;
case X509FindType.FindByTimeExpired:
if (findValue.GetType() != typeof(DateTime))
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindValue));
*((long*) &ft) = ((DateTime) findValue).ToFileTime();
pfnCertCallback1 = new FindProcDelegate(FindTimeNotAfterCallback);
pvCallbackData1 = ft;
break;
case X509FindType.FindByTemplateName:
if (findValue.GetType() != typeof(string))
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindValue));
pvCallbackData1 = (string) findValue;
pfnCertCallback1 = new FindProcDelegate(FindTemplateNameCallback);
break;
case X509FindType.FindByApplicationPolicy:
if (findValue.GetType() != typeof(string))
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindValue));
// If we were passed the friendly name, retrieve the value string.
oidValue = X509Utils.FindOidInfoWithFallback(CAPI.CRYPT_OID_INFO_NAME_KEY, (string) findValue, OidGroup.Policy);
if (oidValue == null) {
oidValue = (string) findValue;
X509Utils.ValidateOidValue(oidValue);
}
pvCallbackData1 = oidValue;
pfnCertCallback1 = new FindProcDelegate(FindApplicationPolicyCallback);
break;
case X509FindType.FindByCertificatePolicy:
if (findValue.GetType() != typeof(string))
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindValue));
// If we were passed the friendly name, retrieve the value string.
oidValue = X509Utils.FindOidInfoWithFallback(CAPI.CRYPT_OID_INFO_NAME_KEY, (string)findValue, OidGroup.Policy);
if (oidValue == null) {
oidValue = (string) findValue;
X509Utils.ValidateOidValue(oidValue);
}
pvCallbackData1 = oidValue;
pfnCertCallback1 = new FindProcDelegate(FindCertificatePolicyCallback);
break;
case X509FindType.FindByExtension:
if (findValue.GetType() != typeof(string))
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindValue));
// If we were passed the friendly name, retrieve the value string.
oidValue = X509Utils.FindOidInfoWithFallback(CAPI.CRYPT_OID_INFO_NAME_KEY, (string)findValue, OidGroup.ExtensionOrAttribute);
if (oidValue == null) {
oidValue = (string) findValue;
X509Utils.ValidateOidValue(oidValue);
}
pvCallbackData1 = oidValue;
pfnCertCallback1 = new FindProcDelegate(FindExtensionCallback);
break;
case X509FindType.FindByKeyUsage:
// The findValue object can be either a friendly name, a X509KeyUsageFlags enum or an integer.
if (findValue.GetType() == typeof(string)) {
CAPI.KEY_USAGE_STRUCT[] KeyUsages = new CAPI.KEY_USAGE_STRUCT[] {
new CAPI.KEY_USAGE_STRUCT("DigitalSignature", CAPI.CERT_DIGITAL_SIGNATURE_KEY_USAGE),
new CAPI.KEY_USAGE_STRUCT("NonRepudiation", CAPI.CERT_NON_REPUDIATION_KEY_USAGE),
new CAPI.KEY_USAGE_STRUCT("KeyEncipherment", CAPI.CERT_KEY_ENCIPHERMENT_KEY_USAGE),
new CAPI.KEY_USAGE_STRUCT("DataEncipherment", CAPI.CERT_DATA_ENCIPHERMENT_KEY_USAGE),
new CAPI.KEY_USAGE_STRUCT("KeyAgreement", CAPI.CERT_KEY_AGREEMENT_KEY_USAGE),
new CAPI.KEY_USAGE_STRUCT("KeyCertSign", CAPI.CERT_KEY_CERT_SIGN_KEY_USAGE),
new CAPI.KEY_USAGE_STRUCT("CrlSign", CAPI.CERT_CRL_SIGN_KEY_USAGE),
new CAPI.KEY_USAGE_STRUCT("EncipherOnly", CAPI.CERT_ENCIPHER_ONLY_KEY_USAGE),
new CAPI.KEY_USAGE_STRUCT("DecipherOnly", CAPI.CERT_DECIPHER_ONLY_KEY_USAGE)
};
for (uint index = 0; index < KeyUsages.Length; index++) {
if (String.Compare(KeyUsages[index].pwszKeyUsage, (string) findValue, StringComparison.OrdinalIgnoreCase) == 0) {
pvCallbackData1 = KeyUsages[index].dwKeyUsageBit;
break;
}
}
if (pvCallbackData1 == null)
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindType));
} else if (findValue.GetType() == typeof(X509KeyUsageFlags)) {
pvCallbackData1 = findValue;
} else if (findValue.GetType() == typeof(uint) || findValue.GetType() == typeof(int)) {
// We got the actual DWORD
pvCallbackData1 = findValue;
} else
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindType));
pfnCertCallback1 = new FindProcDelegate(FindKeyUsageCallback);
break;
case X509FindType.FindBySubjectKeyIdentifier:
if (findValue.GetType() != typeof(string))
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindValue));
pvCallbackData1 = (byte[]) X509Utils.DecodeHexString((string) findValue);
pfnCertCallback1 = new FindProcDelegate(FindSubjectKeyIdentifierCallback);
break;
default:
throw new CryptographicException(SR.GetString(SR.Cryptography_X509_InvalidFindType));
}
// First, create a memory store
SafeCertStoreHandle safeTargetStoreHandle = CAPI.CertOpenStore(new IntPtr(CAPI.CERT_STORE_PROV_MEMORY),
CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING,
IntPtr.Zero,
CAPI.CERT_STORE_ENUM_ARCHIVED_FLAG | CAPI.CERT_STORE_CREATE_NEW_FLAG,
null);
if (safeTargetStoreHandle == null || safeTargetStoreHandle.IsInvalid)
throw new CryptographicException(Marshal.GetLastWin32Error());
// FindByCert will throw an exception in case of failures.
FindByCert(safeSourceStoreHandle,
dwFindType,
pvFindPara,
validOnly,
pfnCertCallback1,
pfnCertCallback2,
pvCallbackData1,
pvCallbackData2,
safeTargetStoreHandle);
pb.Dispose();
return safeTargetStoreHandle;
}
#if FEATURE_CORESYSTEM
[SecuritySafeCritical]
#endif
private static void FindByCert(SafeCertStoreHandle safeSourceStoreHandle,
uint dwFindType,
IntPtr pvFindPara,
bool validOnly,
FindProcDelegate pfnCertCallback1,
FindProcDelegate pfnCertCallback2,
object pvCallbackData1,
object pvCallbackData2,
SafeCertStoreHandle safeTargetStoreHandle) {
int hr = CAPI.S_OK;
SafeCertContextHandle pEnumContext = SafeCertContextHandle.InvalidHandle;
pEnumContext = CAPI.CertFindCertificateInStore(safeSourceStoreHandle,
CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING,
0,
dwFindType,
pvFindPara,
pEnumContext);
while (pEnumContext != null && !pEnumContext.IsInvalid) {
if (pfnCertCallback1 != null) {
hr = pfnCertCallback1(pEnumContext, pvCallbackData1);
if (hr == CAPI.S_FALSE) {
if (pfnCertCallback2 != null)
hr = pfnCertCallback2(pEnumContext, pvCallbackData2);
if (hr == CAPI.S_FALSE) // skip this certificate
goto skip;
}
if (hr != CAPI.S_OK)
break;
}
if (validOnly) {
hr = X509Utils.VerifyCertificate(pEnumContext,
null,
null,
X509RevocationMode.NoCheck,
X509RevocationFlag.ExcludeRoot,
DateTime.Now,
new TimeSpan(0, 0, 0), // default
null,
new IntPtr(CAPI.CERT_CHAIN_POLICY_BASE),
IntPtr.Zero);
if (hr == CAPI.S_FALSE) // skip this certificate
goto skip;
if (hr != CAPI.S_OK)
break;
}
//
// We use CertAddCertificateLinkToStore to keep a link to the original store, so any property changes get
// applied to the original store. This has a limit of 99 links per cert context however.
//
if (!CAPI.CertAddCertificateLinkToStore(safeTargetStoreHandle,
pEnumContext,
CAPI.CERT_STORE_ADD_ALWAYS,
SafeCertContextHandle.InvalidHandle)) {
hr = Marshal.GetHRForLastWin32Error();
break;
}
skip:
// CertFindCertificateInStore always releases the context regardless of success
// or failure so we don't need to manually release it
GC.SuppressFinalize(pEnumContext);
pEnumContext = CAPI.CertFindCertificateInStore(safeSourceStoreHandle,
CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING,
0,
dwFindType,
pvFindPara,
pEnumContext);
}
if (pEnumContext != null && !pEnumContext.IsInvalid)
pEnumContext.Dispose();
if (hr != CAPI.S_FALSE && hr != CAPI.S_OK)
throw new CryptographicException(hr);
}
//
// Callback method to find certificates by subject DN.
//
#if FEATURE_CORESYSTEM
[SecurityCritical]
#endif
private static unsafe int FindSubjectDistinguishedNameCallback(SafeCertContextHandle safeCertContextHandle, object pvCallbackData) {
string rdn = CAPI.GetCertNameInfo(safeCertContextHandle, 0, CAPI.CERT_NAME_RDN_TYPE);
if (String.Compare(rdn, (string) pvCallbackData, StringComparison.OrdinalIgnoreCase) != 0)
return CAPI.S_FALSE;
return CAPI.S_OK;
}
//
// Callback method to find certificates by issuer DN.
//
#if FEATURE_CORESYSTEM
[SecurityCritical]
#endif
private static unsafe int FindIssuerDistinguishedNameCallback(SafeCertContextHandle safeCertContextHandle, object pvCallbackData) {
string rdn = CAPI.GetCertNameInfo(safeCertContextHandle, CAPI.CERT_NAME_ISSUER_FLAG, CAPI.CERT_NAME_RDN_TYPE);
if (String.Compare(rdn, (string) pvCallbackData, StringComparison.OrdinalIgnoreCase) != 0)
return CAPI.S_FALSE;
return CAPI.S_OK;
}
//
// Callback method to find certificates by serial number.
// This can be useful when using XML Digital Signature and X509Data.
//
#if FEATURE_CORESYSTEM
[SecurityCritical]
#endif
private static unsafe int FindSerialNumberCallback(SafeCertContextHandle safeCertContextHandle, object pvCallbackData) {
CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle());
CAPI.CERT_INFO pCertInfo = (CAPI.CERT_INFO) Marshal.PtrToStructure(pCertContext.pCertInfo, typeof(CAPI.CERT_INFO));
byte[] hex = new byte[pCertInfo.SerialNumber.cbData];
Marshal.Copy(pCertInfo.SerialNumber.pbData, hex, 0, hex.Length);
int size = X509Utils.GetHexArraySize(hex);
byte[] serialNumber = (byte[]) pvCallbackData;
if (serialNumber.Length != size)
return CAPI.S_FALSE;
for (int index = 0; index < serialNumber.Length; index++) {
if (serialNumber[index] != hex[index])
return CAPI.S_FALSE;
}
return CAPI.S_OK;
}
//
// Callback method to find certificates by validity time.
// The callback data has to be a UTC FILETEME.
//
#if FEATURE_CORESYSTEM
[SecurityCritical]
#endif
private static unsafe int FindTimeValidCallback(SafeCertContextHandle safeCertContextHandle, object pvCallbackData) {
_FILETIME ft = (_FILETIME) pvCallbackData;
CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle());
if (CAPI.CertVerifyTimeValidity(ref ft, pCertContext.pCertInfo) == 0)
return CAPI.S_OK;
return CAPI.S_FALSE;
}
//
// Callback method to find certificates expired at a certain DateTime.
// The callback data has to be a UTC FILETEME.
//
#if FEATURE_CORESYSTEM
[SecurityCritical]
#endif
private static unsafe int FindTimeNotAfterCallback(SafeCertContextHandle safeCertContextHandle, object pvCallbackData) {
_FILETIME ft = (_FILETIME) pvCallbackData;
CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle());
if (CAPI.CertVerifyTimeValidity(ref ft, pCertContext.pCertInfo) == 1)
return CAPI.S_OK;
return CAPI.S_FALSE;
}
//
// Callback method to find certificates effective after a certain DateTime.
// The callback data has to be a UTC FILETEME.
//
#if FEATURE_CORESYSTEM
[SecurityCritical]
#endif
private static unsafe int FindTimeNotBeforeCallback(SafeCertContextHandle safeCertContextHandle, object pvCallbackData) {
_FILETIME ft = (_FILETIME) pvCallbackData;
CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle());
if (CAPI.CertVerifyTimeValidity(ref ft, pCertContext.pCertInfo) == -1)
return CAPI.S_OK;
return CAPI.S_FALSE;
}
//
// Callback method to find certificates by template name.
// The template name can have 2 different formats: V1 format (<= Win2K) is just a string
// V2 format (XP only) can be a friendly name or an OID.
// An example of Template Name can be "ClientAuth".
//
#if FEATURE_CORESYSTEM
[SecurityCritical]
#endif
private static unsafe int FindTemplateNameCallback(SafeCertContextHandle safeCertContextHandle, object pvCallbackData) {
IntPtr pV1Template = IntPtr.Zero;
IntPtr pV2Template = IntPtr.Zero;
CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle());
CAPI.CERT_INFO pCertInfo = (CAPI.CERT_INFO) Marshal.PtrToStructure(pCertContext.pCertInfo, typeof(CAPI.CERT_INFO));
pV1Template = CAPI.CertFindExtension(CAPI.szOID_ENROLL_CERTTYPE_EXTENSION,
pCertInfo.cExtension,
pCertInfo.rgExtension);
pV2Template = CAPI.CertFindExtension(CAPI.szOID_CERTIFICATE_TEMPLATE,
pCertInfo.cExtension,
pCertInfo.rgExtension);
if (pV1Template == IntPtr.Zero && pV2Template == IntPtr.Zero)
return CAPI.S_FALSE;
if (pV1Template != IntPtr.Zero) {
CAPI.CERT_EXTENSION extension = (CAPI.CERT_EXTENSION) Marshal.PtrToStructure(pV1Template, typeof(CAPI.CERT_EXTENSION));
byte[] rawData = new byte[extension.Value.cbData];
Marshal.Copy(extension.Value.pbData, rawData, 0, rawData.Length);
uint cbDecoded = 0;
SafeLocalAllocHandle decoded = null;
// Decode the extension.
bool result = CAPI.DecodeObject(new IntPtr(CAPI.X509_UNICODE_ANY_STRING),
rawData,
out decoded,
out cbDecoded);
if (result) {
CAPI.CERT_NAME_VALUE pNameValue = (CAPI.CERT_NAME_VALUE) Marshal.PtrToStructure(decoded.DangerousGetHandle(), typeof(CAPI.CERT_NAME_VALUE));
string s = Marshal.PtrToStringUni(pNameValue.Value.pbData);
if (String.Compare(s, (string) pvCallbackData, StringComparison.OrdinalIgnoreCase) == 0)
return CAPI.S_OK;
}
}
if (pV2Template != IntPtr.Zero) {
CAPI.CERT_EXTENSION extension = (CAPI.CERT_EXTENSION) Marshal.PtrToStructure(pV2Template, typeof(CAPI.CERT_EXTENSION));
byte[] rawData = new byte[extension.Value.cbData];
Marshal.Copy(extension.Value.pbData, rawData, 0, rawData.Length);
uint cbDecoded = 0;
SafeLocalAllocHandle decoded = null;
// Decode the extension.
bool result = CAPI.DecodeObject(new IntPtr(CAPI.X509_CERTIFICATE_TEMPLATE),
rawData,
out decoded,
out cbDecoded);
if (result) {
CAPI.CERT_TEMPLATE_EXT pTemplate = (CAPI.CERT_TEMPLATE_EXT) Marshal.PtrToStructure(decoded.DangerousGetHandle(), typeof(CAPI.CERT_TEMPLATE_EXT));
// If we were passed the friendly name, retrieve the value string.
string oidValue = X509Utils.FindOidInfoWithFallback(CAPI.CRYPT_OID_INFO_NAME_KEY, (string)pvCallbackData, OidGroup.Template);
if (oidValue == null)
oidValue = (string) pvCallbackData;
if (String.Compare(pTemplate.pszObjId, oidValue, StringComparison.OrdinalIgnoreCase) == 0)
return CAPI.S_OK;
}
}
return CAPI.S_FALSE;
}
//
// Callback method to find certificates by application policy (also known as EKU)
// An example of application policy can be: "Encrypting File System"
//
#if FEATURE_CORESYSTEM
[SecurityCritical]
#endif
private static unsafe int FindApplicationPolicyCallback(SafeCertContextHandle safeCertContextHandle, object pvCallbackData) {
string eku = (string) pvCallbackData;
if (eku.Length == 0)
return CAPI.S_FALSE;
IntPtr pCertContext = safeCertContextHandle.DangerousGetHandle();
int cNumOIDs = 0;
uint cbOIDs = 0;
SafeLocalAllocHandle rghOIDs = SafeLocalAllocHandle.InvalidHandle;
if (!CAPI.CertGetValidUsages(1, new IntPtr(&pCertContext), new IntPtr(&cNumOIDs), rghOIDs, new IntPtr(&cbOIDs)))
return CAPI.S_FALSE;
rghOIDs = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(cbOIDs));
if (!CAPI.CertGetValidUsages(1, new IntPtr(&pCertContext), new IntPtr(&cNumOIDs), rghOIDs, new IntPtr(&cbOIDs)))
return CAPI.S_FALSE;
// -1 means the certificate is good for all usages.
if (cNumOIDs == -1)
return CAPI.S_OK;
for (int index = 0; index < cNumOIDs; index++) {
IntPtr pszOid = Marshal.ReadIntPtr(new IntPtr((long) rghOIDs.DangerousGetHandle() + index * Marshal.SizeOf(typeof(IntPtr))));
string oidValue = Marshal.PtrToStringAnsi(pszOid);
if (String.Compare(eku, oidValue, StringComparison.OrdinalIgnoreCase) == 0)
return CAPI.S_OK;
}
return CAPI.S_FALSE;
}
//
// Callback method to find certificates by certificate policy.
// This is only recognized in XP platforms. However, passing in an OID value should work on downlevel platforms as well.
//
#if FEATURE_CORESYSTEM
[SecurityCritical]
#endif
private static unsafe int FindCertificatePolicyCallback(SafeCertContextHandle safeCertContextHandle, object pvCallbackData) {
string certPolicy = (string) pvCallbackData;
if (certPolicy.Length == 0)
return CAPI.S_FALSE;
CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle());
CAPI.CERT_INFO pCertInfo = (CAPI.CERT_INFO) Marshal.PtrToStructure(pCertContext.pCertInfo, typeof(CAPI.CERT_INFO));
IntPtr pExtension = CAPI.CertFindExtension(CAPI.szOID_CERT_POLICIES,
pCertInfo.cExtension,
pCertInfo.rgExtension);
if (pExtension == IntPtr.Zero)
return CAPI.S_FALSE;
CAPI.CERT_EXTENSION extension = (CAPI.CERT_EXTENSION) Marshal.PtrToStructure(pExtension, typeof(CAPI.CERT_EXTENSION));
byte[] rawData = new byte[extension.Value.cbData];
Marshal.Copy(extension.Value.pbData, rawData, 0, rawData.Length);
uint cbDecoded = 0;
SafeLocalAllocHandle decoded = null;
// Decode the extension.
bool result = CAPI.DecodeObject(new IntPtr(CAPI.X509_CERT_POLICIES),
rawData,
out decoded,
out cbDecoded);
if (result) {
CAPI.CERT_POLICIES_INFO pInfo = (CAPI.CERT_POLICIES_INFO) Marshal.PtrToStructure(decoded.DangerousGetHandle(), typeof(CAPI.CERT_POLICIES_INFO));
for (int index = 0; index < pInfo.cPolicyInfo; index++) {
IntPtr pPolicyInfoPtr = new IntPtr((long) pInfo.rgPolicyInfo + index * Marshal.SizeOf(typeof(CAPI.CERT_POLICY_INFO)));
CAPI.CERT_POLICY_INFO pPolicyInfo = (CAPI.CERT_POLICY_INFO) Marshal.PtrToStructure(pPolicyInfoPtr, typeof(CAPI.CERT_POLICY_INFO));
if (String.Compare(certPolicy, pPolicyInfo.pszPolicyIdentifier, StringComparison.OrdinalIgnoreCase) == 0)
return CAPI.S_OK;
}
}
return CAPI.S_FALSE;
}
//
// Callback method to find certificates that have a particular extension.
// The callback data can be either an OID friendly name or value (all should be ANSI strings).
//
#if FEATURE_CORESYSTEM
[SecurityCritical]
#endif
private static unsafe int FindExtensionCallback(SafeCertContextHandle safeCertContextHandle, object pvCallbackData) {
CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle());
CAPI.CERT_INFO pCertInfo = (CAPI.CERT_INFO) Marshal.PtrToStructure(pCertContext.pCertInfo, typeof(CAPI.CERT_INFO));
IntPtr pExtension = CAPI.CertFindExtension((string) pvCallbackData,
pCertInfo.cExtension,
pCertInfo.rgExtension);
if (pExtension == IntPtr.Zero)
return CAPI.S_FALSE;
return CAPI.S_OK;
}
//
// Callback method to find certificates that have a particular Key Usage.
// The callback data can be either a string (example: "KeyEncipherment") or a DWORD which can have multiple bits set in it.
// If the callback data is a string, we can achieve the effect of a bit union by calling it multiple times, each time
// further restricting the set of selected certificates.
//
#if FEATURE_CORESYSTEM
[SecurityCritical]
#endif
private static unsafe int FindKeyUsageCallback(SafeCertContextHandle safeCertContextHandle, object pvCallbackData) {
CAPI.CERT_CONTEXT pCertContext = *((CAPI.CERT_CONTEXT*) safeCertContextHandle.DangerousGetHandle());
uint dwUsages = 0;
if (!CAPI.CertGetIntendedKeyUsage(CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING,
pCertContext.pCertInfo,
new IntPtr(&dwUsages),
4 /* sizeof(DWORD) */))
return CAPI.S_OK; // no key usage means it is valid for all key usages.
uint dwCheckUsage = Convert.ToUInt32(pvCallbackData, null);
if ((dwUsages & dwCheckUsage) == dwCheckUsage)
return CAPI.S_OK;
return CAPI.S_FALSE;
}
//
// Callback method to find certificates by subject key identifier.
// This can be useful when using XML Digital Signature and X509Data.
//
#if FEATURE_CORESYSTEM
[SecurityCritical]
#endif
private static unsafe int FindSubjectKeyIdentifierCallback(SafeCertContextHandle safeCertContextHandle, object pvCallbackData) {
SafeLocalAllocHandle ptr = SafeLocalAllocHandle.InvalidHandle;
// We look for the Key Id extended property
// this will first look if there is a V3 SKI extension
// and then if that fails, It will return the Key Id extended property.
uint cbData = 0;
if (!CAPI.CertGetCertificateContextProperty(safeCertContextHandle,
CAPI.CERT_KEY_IDENTIFIER_PROP_ID,
ptr,
ref cbData))
return CAPI.S_FALSE;
ptr = CAPI.LocalAlloc(CAPI.LMEM_FIXED, new IntPtr(cbData));
if (!CAPI.CertGetCertificateContextProperty(safeCertContextHandle,
CAPI.CERT_KEY_IDENTIFIER_PROP_ID,
ptr,
ref cbData))
return CAPI.S_FALSE;
byte[] subjectKeyIdentifier = (byte[]) pvCallbackData;
if (subjectKeyIdentifier.Length != cbData)
return CAPI.S_FALSE;
byte[] hex = new byte[cbData];
Marshal.Copy(ptr.DangerousGetHandle(), hex, 0, hex.Length);
ptr.Dispose();
for (uint index = 0; index < cbData; index++) {
if (subjectKeyIdentifier[index] != hex[index])
return CAPI.S_FALSE;
}
return CAPI.S_OK;
}
private const uint X509_STORE_CONTENT_FLAGS =
(CAPI.CERT_QUERY_CONTENT_FLAG_CERT |
CAPI.CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT |
CAPI.CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
CAPI.CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED |
CAPI.CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED |
CAPI.CERT_QUERY_CONTENT_FLAG_PFX |
CAPI.CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE);
#if FEATURE_CORESYSTEM
[SecuritySafeCritical]
#endif
[ResourceExposure(ResourceScope.None)]
#if !FEATURE_CORESYSTEM
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
#endif
private unsafe static SafeCertStoreHandle LoadStoreFromBlob(byte[] rawData, string password, uint dwFlags, bool persistKeyContainers) {
uint contentType = 0;
SafeCertStoreHandle safeCertStoreHandle = SafeCertStoreHandle.InvalidHandle;
if (!CAPI.CryptQueryObject(CAPI.CERT_QUERY_OBJECT_BLOB,
rawData,
X509_STORE_CONTENT_FLAGS,
CAPI.CERT_QUERY_FORMAT_FLAG_ALL,
0,
IntPtr.Zero,
new IntPtr(&contentType),
IntPtr.Zero,
ref safeCertStoreHandle,
IntPtr.Zero,
IntPtr.Zero))
throw new CryptographicException(Marshal.GetLastWin32Error());
if (contentType == CAPI.CERT_QUERY_CONTENT_PFX) {
safeCertStoreHandle.Dispose();
safeCertStoreHandle = CAPI.PFXImportCertStore(CAPI.CERT_QUERY_OBJECT_BLOB,
rawData,
password,
dwFlags,
persistKeyContainers);
}
if (safeCertStoreHandle == null || safeCertStoreHandle.IsInvalid)
throw new CryptographicException(Marshal.GetLastWin32Error());
return safeCertStoreHandle;
}
#if FEATURE_CORESYSTEM
[SecuritySafeCritical]
#endif
[ResourceExposure(ResourceScope.Machine)]
#if !FEATURE_CORESYSTEM
[ResourceConsumption(ResourceScope.Machine)]
#endif
private unsafe static SafeCertStoreHandle LoadStoreFromFile(string fileName, string password, uint dwFlags, bool persistKeyContainers) {
uint contentType = 0;
SafeCertStoreHandle safeCertStoreHandle = SafeCertStoreHandle.InvalidHandle;
if (!CAPI.CryptQueryObject(CAPI.CERT_QUERY_OBJECT_FILE,
fileName,
X509_STORE_CONTENT_FLAGS,
CAPI.CERT_QUERY_FORMAT_FLAG_ALL,
0,
IntPtr.Zero,
new IntPtr(&contentType),
IntPtr.Zero,
ref safeCertStoreHandle,
IntPtr.Zero,
IntPtr.Zero))
throw new CryptographicException(Marshal.GetLastWin32Error());
if (contentType == CAPI.CERT_QUERY_CONTENT_PFX) {
safeCertStoreHandle.Dispose();
safeCertStoreHandle = CAPI.PFXImportCertStore(CAPI.CERT_QUERY_OBJECT_FILE,
fileName,
password,
dwFlags,
persistKeyContainers);
}
if (safeCertStoreHandle == null || safeCertStoreHandle.IsInvalid)
throw new CryptographicException(Marshal.GetLastWin32Error());
return safeCertStoreHandle;
}
}
public sealed class X509Certificate2Enumerator : IEnumerator {
private IEnumerator baseEnumerator;
private X509Certificate2Enumerator() {}
internal X509Certificate2Enumerator(X509Certificate2Collection mappings) {
this.baseEnumerator = ((IEnumerable) mappings).GetEnumerator();
}
public X509Certificate2 Current {
get {
return ((X509Certificate2)(baseEnumerator.Current));
}
}
/// <internalonly/>
object IEnumerator.Current {
get {
return baseEnumerator.Current;
}
}
public bool MoveNext() {
return baseEnumerator.MoveNext();
}
/// <internalonly/>
bool IEnumerator.MoveNext() {
return baseEnumerator.MoveNext();
}
public void Reset() {
baseEnumerator.Reset();
}
/// <internalonly/>
void IEnumerator.Reset() {
baseEnumerator.Reset();
}
}
}
|