News  [SoftwareSite

Latest News
Older News
RSS Feed
 
Complete Projects
Useful Classes
Top Downloads
Message Board
AllAPI.net
 
Send Comments
Software License
Mentalis.org Buttons
Donate
 
Forums -> Security Library Forum
 
GetCertificates() Implementation  
by Daniel B
posted on 2004/12/16

I noticed that the CertificateChain constructors call CertGetCertificateChain() to build the chain, and a reference to the chain is stored as m_Handle. However, when calling GetCertificates(), it looks like m_Handle isn't used, and the chain is retrieved again using a different method, namely, starting with the end certificate and repeatedly calling CertGetIssuerCertificateFromStore().

I was just curious whether these two different methods of getting the chain will always, in all cases, return the same exact chain? Was there a specific reason for getting it again a second time? (Just curious.)

The reason I ask is that I'm wanting to check the validity of a chain at a specific time. To do this, I'm constructing a CertificateChain object, then calling VerifyChain() with flags set to IgnoreTimeNotValid, then calling GetCertificates() and looping through and manually checking the validity period on each certificate against the particular time. I just want to make absolutely sure that in all cases I'm actually checking the validity of the same certificates as those checked in VerifyChain(). (I don't know much about the chain building process, but was under the impression that there can possibly be multiple chains of differing quality, that certificates can be located outside the certificate store, such as via the Authority Information Access extension, etc, and was just wondering if the two methods of getting the chain work the same way with respect to all these and other possibilities).

Thanks much for your time. The library has proved very useful so far.

Daniel

by Pieter Philippaerts [Pieter at mentalis dot org]
posted on 2004/12/25

That's a good question, and I can't answer you with 100% certainty. In most cases you'll get the same chain, but I can't guarantee that they will always be the same.

by Daniel Boggs [daniel dot boggs at intellisafe dot com]
posted on 2005/01/04

I decided to write an alternate method to GetCertificates(), to get the certificates directly from the certificate context instead of going back and getting them a different way. I thought I'd share it here. It hasn't been extensively tested though. Feel free to comment if you see any problems with it.

In the CertificateChain.cs file, added the following method:

/// <summary>
/// Returns the list of certificates in this <see cref="CertificateChain"/>.
/// </summary>
/// <returns>An array of <see cref="Certificate"/> instances.</returns>
public virtual Certificate[] GetCertificates2() {
CERT_CHAIN_CONTEXT certChainCtx = (CERT_CHAIN_CONTEXT)Marshal.PtrToStructure(m_Handle, typeof(CERT_CHAIN_CONTEXT));

IntPtr current = Marshal.ReadIntPtr(certChainCtx.rgpChain);
CERT_SIMPLE_CHAIN[] chains = new CERT_SIMPLE_CHAIN[certChainCtx.cChain];
Type type = typeof(CERT_SIMPLE_CHAIN);
int size = Marshal.SizeOf(type);
for (int i = 0; i < chains.Length; i++)
{
chains[i] = (CERT_SIMPLE_CHAIN)Marshal.PtrToStructure(current, type);
current = (IntPtr)((long)current + size);
}

if (chains.Length == 0)
{
return new Certificate[0];
}

// just use the first chain
CERT_SIMPLE_CHAIN chain = chains[0];

current = Marshal.ReadIntPtr(chain.rgpElement);
CERT_CHAIN_ELEMENT[] elts = new CERT_CHAIN_ELEMENT[chain.cElement];
type = typeof(CERT_CHAIN_ELEMENT);
size = Marshal.SizeOf(type);
for (int i = 0; i < elts.Length; i++)
{
elts[i] = (CERT_CHAIN_ELEMENT)Marshal.PtrToStructure(current, type);
current = (IntPtr)((long)current + size);
}

Certificate[] certs = new Certificate[elts.Length];
for (int i = 0; i < elts.Length; i++)
{
certs[i] = new Certificate(elts[i].pCertContext, true);
}

return certs;
}

In the SecurityStructures.cs file, needed to add the following structures:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
struct CERT_CHAIN_CONTEXT
{
public int cbSize;
public CERT_TRUST_STATUS TrustStatus;
public int cChain;
public IntPtr rgpChain;
public int cLowerQualityChainContext;
public IntPtr rgpLowerQualityChainContext;
public bool fHasRevocationFreshnessTime;
public int dwRevocationFreshnessTime;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
struct CERT_TRUST_STATUS
{
public int dwErrorStatus;
public int dwInfoStatus;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
struct CERT_SIMPLE_CHAIN
{
public int cbSize;
public CERT_TRUST_STATUS TrustStatus;
public int cElement;
public IntPtr rgpElement;
public IntPtr pTrustListInfo;
public bool fHasRevocationFreshnessTime;
public int dwRevocationFreshnessTime;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
struct CERT_CHAIN_ELEMENT
{
public int cbSize;
public IntPtr pCertContext;
public CERT_TRUST_STATUS TrustStatus;
public IntPtr pRevocationInfo;
public IntPtr pIssuanceUsage;
public IntPtr pApplicationUsage;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszExtendedErrorInfo;
}

by Daniel B
posted on 2005/01/04

Note that the above code only seems to work with WinXP. Errors encountered on Win2K.

by Daniel B
posted on 2005/01/05

Once I applied the latest critical updates via Windows Update to the Windows 2000 machine, it started working. So the code above seems to work on both XP and 2000, as long as you've got the latest Windows updates (I'm not sure which one fixed the problem though).

Also, I ended up writing another method which accomplishes the same thing as the one above, but doesn't require any of the associated structures:

/// <summary>
/// Returns the list of certificates in this <see cref="CertificateChain"/>.
/// </summary>
/// <returns>An array of <see cref="Certificate"/> instances.</returns>
public virtual Certificate[] GetCertificates2()
{
IntPtr current = m_Handle;

// read rgpChain from CERT_CHAIN_CONTEXT structure
int cChain = Marshal.ReadInt32(current, 12);
if (cChain == 0)
{
return new Certificate[0];
}
IntPtr rgpChain = Marshal.ReadIntPtr(current, 16);

// read rgpElement from first CERT_SIMPLE_CHAIN structure
current = Marshal.ReadIntPtr(rgpChain);
int cElement = Marshal.ReadInt32(current, 12);
IntPtr rgpElement = Marshal.ReadIntPtr(current, 16);

// read elements from CERT_CHAIN_ELEMENT[]
current = Marshal.ReadIntPtr(rgpElement);
Certificate[] certs = new Certificate[cElement];
for (int i = 0; i < cElement; i++)
{
int cbSize = Marshal.ReadInt32(current, 0);
IntPtr pCertContext = Marshal.ReadIntPtr(current, 4);
certs[i] = new Certificate(pCertContext, true);
current = (IntPtr)((long)current + cbSize);
}

return certs;
}

by Daniel B
posted on 2005/01/05

One more note FYI:

Apparently, the Windows hotfix that fixed the error mentioned above is security update KB835732. At least, when I uninstalled the hotfix, the error came back, and when I reinstalled the hotfix, the error went away. I couldn't find anything in the documentation on that hotfix that indicated what might have changed to fix this problem, though.

 

Copyright © 2002-2007, The Mentalis.org Team. All rights reserved.
This site is located at http://www.mentalis.org/
Send comments to the webmaster.