Problems when P/Invoking CertCreateSelfSignCertificate

1k views Asked by At

I was following this article (in it there is a link to a .cs file at the bottom of the page) to generate a self-signed X509Certificate2. The code in the article works but now I want to extend it. I am trying to pass the optional argument, _In_opt_ PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, into CertCreateSelfSignCertificate.

I have created this structure for it:

struct CryptoApiBlob
{
    public Int32 cbData;
    public IntPtr pbData;
}

struct CryptAlgorithmIdentifier {
    public String pszObjId;
    public CryptoApiBlob Parameters;
}

The code I am trying to use to create it is:

CryptAlgorithmIdentifier algorithm = new CryptAlgorithmIdentifier { pszObjId = "szOID_NIST_AES256_CBC", Parameters = new CryptoApiBlob { cbData = 0 } };
algorithmPointer = Marshal.AllocHGlobal(Marshal.SizeOf(algorithm));
Marshal.StructureToPtr(algorithm, algorithmPointer, false);

I then pass the algorithmPointer into the method.

I get this error when I try to pass it into CertCreateSelfSignCertificate:

An unhandled exception of type 'System.Runtime.InteropServices.COMException' occurred in mscorlib.dll

Additional information: ASN1 bad arguments to function call. (Exception from HRESULT: 0x80093109)

Does anyone happen to know why this happens, or can see any problems with the way I've defined my structure or allocated it in memory?

2

There are 2 answers

5
Captain Coder On BEST ANSWER

As @Luaan noted, strings can be tricky to marshal correctly in p/invoke, it's often easiest to avoid p/invoke interop when you can. However I was still curious what was going wrong here.

From the MSDN docs on PCRYPT_ALGORITHM_IDENTIFIER it looks as though you should pass in the actual OID of the algorithm "2.16.840.1.101.3.4.1.42" in this case. The szOID_NIST_AES256_CBC that is in the list there is only the C/C++ identifier (or macro) that expands to said OID string.

0
atanamir On

This question is old, but I thought I'd share my insight. MSDN lists CRYPT_ALGORITHM_IDENTIFIER as:

typedef struct _CRYPT_ALGORITHM_IDENTIFIER {
    LPSTR            pszObjId;
    CRYPT_OBJID_BLOB Parameters;
} CRYPT_ALGORITHM_IDENTIFIER, *PCRYPT_ALGORITHM_IDENTIFIER;

The important thing to note that it's an LPSTR, not a LPWSTR. So the string needs to be marshalled as ANSI. Unless specified explicitly, .NET will marshal the strings as unicode. So our corresponding struct needs additional decoration:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct AlgorithmIdentifier
{
    public string ObjectId;
    public CryptoBlob Parameters;
}

Adding CharSet into the StructLayout attribute will make .NET marshal the strings in the required encoding. Then it can be used in a straightforward manner:

var algId = new AlgorithmIdentifier
{
    ObjectId = "1.2.840.113549.1.1.5" // szOID_RSA_SHA1RSA
};