Implement encoding/decoding of CERT_ALT_NAME_INFOs and CRL_INFOs.

diff --git a/dlls/crypt32/encode.c b/dlls/crypt32/encode.c
index ad4fb4b..121569f 100644
--- a/dlls/crypt32/encode.c
+++ b/dlls/crypt32/encode.c
@@ -35,6 +35,9 @@
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
+
+#define NONAMELESSUNION
+
 #include "windef.h"
 #include "winbase.h"
 #include "excpt.h"
@@ -59,6 +62,9 @@
 #define ASN_UTCTIME         (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x17)
 #define ASN_GENERALTIME     (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x18)
 
+#define ASN_FLAGS_MASK 0xf0
+#define ASN_TYPE_MASK  0x0f
+
 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
 
 static const WCHAR szDllName[] = { 'D','l','l',0 };
@@ -119,7 +125,11 @@
 static BOOL WINAPI CRYPT_AsnDecodePubKeyInfo(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
-static BOOL WINAPI CRYPT_AsnDecodeExtensions(DWORD dwCertEncodingType,
+/* Like CRYPT_AsnDecodeExtensions, except assumes rgExtension is set ahead of
+ * time, doesn't do memory allocation, and doesn't do exception handling.
+ * (This isn't intended to be the externally-called one.)
+ */
+static BOOL WINAPI CRYPT_AsnDecodeExtensionsInternal(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
 static BOOL WINAPI CRYPT_AsnDecodeOid(const BYTE *pbEncoded, DWORD cbEncoded,
@@ -419,9 +429,9 @@
     HMODULE lib;
     CryptEncodeObjectFunc pCryptEncodeObject;
 
-    TRACE("(0x%08lx, %s, %p, %p, %p)\n",
-     dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) :
-     "(integer value)", pvStructInfo, pbEncoded, pcbEncoded);
+    TRACE("(0x%08lx, %s, %p, %p, %p)\n", dwCertEncodingType,
+     debugstr_a(lpszStructType), pvStructInfo, pbEncoded,
+     pcbEncoded);
 
     if (!pbEncoded && !pcbEncoded)
     {
@@ -835,6 +845,156 @@
     return ret;
 }
 
+static BOOL WINAPI CRYPT_AsnEncodeCRLEntry(const CRL_ENTRY *entry,
+ BYTE *pbEncoded, DWORD *pcbEncoded)
+{
+    struct AsnEncodeSequenceItem items[3] = {
+     { &entry->SerialNumber,   CRYPT_AsnEncodeInteger, 0 },
+     { &entry->RevocationDate, CRYPT_AsnEncodeChoiceOfTime, 0 },
+     { 0 }
+    };
+    DWORD cItem = 2;
+    BOOL ret;
+
+    TRACE("%p, %p, %p\n", entry, pbEncoded, pcbEncoded);
+
+    if (entry->cExtension)
+    {
+        items[cItem].pvStructInfo = &entry->cExtension;
+        items[cItem].encodeFunc = CRYPT_AsnEncodeExtensions;
+        cItem++;
+    }
+
+    ret = CRYPT_AsnEncodeSequence(X509_ASN_ENCODING, items, cItem, 0, NULL,
+     pbEncoded, pcbEncoded);
+
+    TRACE("returning %d (%08lx)\n", ret, GetLastError());
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnEncodeCRLEntries(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
+ PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
+{
+    DWORD cCRLEntry = *(const DWORD *)pvStructInfo;
+    DWORD bytesNeeded, dataLen, lenBytes, i;
+    const CRL_ENTRY *rgCRLEntry = *(const CRL_ENTRY **)
+     ((const BYTE *)pvStructInfo + sizeof(DWORD));
+    BOOL ret = TRUE;
+
+    for (i = 0, dataLen = 0; ret && i < cCRLEntry; i++)
+    {
+        DWORD size;
+
+        ret = CRYPT_AsnEncodeCRLEntry(&rgCRLEntry[i], NULL, &size);
+        if (ret)
+            dataLen += size;
+    }
+    CRYPT_EncodeLen(dataLen, NULL, &lenBytes);
+    bytesNeeded = 1 + lenBytes + dataLen;
+    if (!pbEncoded)
+        *pcbEncoded = bytesNeeded;
+    else
+    {
+        if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded,
+         pcbEncoded, bytesNeeded)))
+        {
+            if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
+                pbEncoded = *(BYTE **)pbEncoded;
+            *pbEncoded++ = ASN_SEQUENCEOF;
+            CRYPT_EncodeLen(dataLen, pbEncoded, &lenBytes);
+            pbEncoded += lenBytes;
+            for (i = 0; i < cCRLEntry; i++)
+            {
+                DWORD size = dataLen;
+
+                ret = CRYPT_AsnEncodeCRLEntry(&rgCRLEntry[i], pbEncoded, &size);
+                pbEncoded += size;
+                dataLen -= size;
+            }
+        }
+    }
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnEncodeCRLVersion(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
+ PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
+{
+    const DWORD *ver = (const DWORD *)pvStructInfo;
+    BOOL ret;
+
+    /* CRL_V1 is not encoded */
+    if (*ver == CRL_V1)
+    {
+        *pcbEncoded = 0;
+        ret = TRUE;
+    }
+    else
+        ret = CRYPT_AsnEncodeInt(dwCertEncodingType, X509_INTEGER, ver,
+         dwFlags, pEncodePara, pbEncoded, pcbEncoded);
+    return ret;
+}
+
+/* Like in Windows, this blithely ignores the validity of the passed-in
+ * CRL_INFO, and just encodes it as-is.  The resulting encoded data may not
+ * decode properly, see CRYPT_AsnDecodeCRLInfo.
+ */
+static BOOL WINAPI CRYPT_AsnEncodeCRLInfo(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
+ PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
+{
+    BOOL ret;
+
+    __TRY
+    {
+        const CRL_INFO *info = (const CRL_INFO *)pvStructInfo;
+        struct AsnEncodeSequenceItem items[7] = {
+         { &info->dwVersion,          CRYPT_AsnEncodeCRLVersion, 0 },
+         { &info->SignatureAlgorithm, CRYPT_AsnEncodeAlgorithmId, 0 },
+         { &info->Issuer,             CRYPT_CopyEncodedBlob, 0 },
+         { &info->ThisUpdate,         CRYPT_AsnEncodeChoiceOfTime, 0 },
+         { 0 }
+        };
+        struct AsnConstructedItem constructed = { 0 };
+        DWORD cItem = 4;
+
+        if (info->NextUpdate.dwLowDateTime || info->NextUpdate.dwHighDateTime)
+        {
+            items[cItem].pvStructInfo = &info->NextUpdate;
+            items[cItem].encodeFunc = CRYPT_AsnEncodeChoiceOfTime;
+            cItem++;
+        }
+        if (info->cCRLEntry)
+        {
+            items[cItem].pvStructInfo = &info->cCRLEntry;
+            items[cItem].encodeFunc = CRYPT_AsnEncodeCRLEntries;
+            cItem++;
+        }
+        if (info->cExtension)
+        {
+            /* FIXME: is this really constructed? if so, is this the right tag?
+             */
+            constructed.tag = 3;
+            constructed.pvStructInfo = &info->cExtension;
+            constructed.encodeFunc = CRYPT_AsnEncodeExtensions;
+            items[cItem].pvStructInfo = &constructed;
+            items[cItem].encodeFunc = CRYPT_AsnEncodeConstructed;
+            cItem++;
+        }
+
+        ret = CRYPT_AsnEncodeSequence(dwCertEncodingType, items, cItem,
+         dwFlags, pEncodePara, pbEncoded, pcbEncoded);
+    }
+    __EXCEPT(page_fault)
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
 static BOOL CRYPT_AsnEncodeExtension(CERT_EXTENSION *ext, BYTE *pbEncoded,
  DWORD *pcbEncoded)
 {
@@ -1328,6 +1488,171 @@
     return ret;
 }
 
+static BOOL CRYPT_AsnEncodeAltNameEntry(const CERT_ALT_NAME_ENTRY *entry,
+ BYTE *pbEncoded, DWORD *pcbEncoded)
+{
+    BOOL ret;
+    DWORD dataLen;
+
+    ret = TRUE;
+    switch (entry->dwAltNameChoice)
+    {
+    case CERT_ALT_NAME_RFC822_NAME:
+    case CERT_ALT_NAME_DNS_NAME:
+    case CERT_ALT_NAME_URL:
+        if (entry->u.pwszURL)
+        {
+            DWORD i;
+
+            /* Not + 1: don't encode the NULL-terminator */
+            dataLen = lstrlenW(entry->u.pwszURL);
+            for (i = 0; ret && i < dataLen; i++)
+            {
+                if (entry->u.pwszURL[i] > 0x7f)
+                {
+                    SetLastError(CRYPT_E_INVALID_IA5_STRING);
+                    ret = FALSE;
+                    *pcbEncoded = i;
+                }
+            }
+        }
+        else
+            dataLen = 0;
+        break;
+    case CERT_ALT_NAME_IP_ADDRESS:
+        dataLen = entry->u.IPAddress.cbData;
+        break;
+    case CERT_ALT_NAME_REGISTERED_ID:
+        /* FIXME: encode OID */
+    case CERT_ALT_NAME_OTHER_NAME:
+    case CERT_ALT_NAME_DIRECTORY_NAME:
+        FIXME("name type %ld unimplemented\n", entry->dwAltNameChoice);
+        return FALSE;
+    default:
+        SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
+        return FALSE;
+    }
+    if (ret)
+    {
+        DWORD bytesNeeded, lenBytes;
+
+        CRYPT_EncodeLen(dataLen, NULL, &lenBytes);
+        bytesNeeded = 1 + dataLen + lenBytes;
+        if (!pbEncoded)
+            *pcbEncoded = bytesNeeded;
+        else if (*pcbEncoded < bytesNeeded)
+        {
+            SetLastError(ERROR_MORE_DATA);
+            *pcbEncoded = bytesNeeded;
+            ret = FALSE;
+        }
+        else
+        {
+            *pbEncoded++ = ASN_CONTEXT | (entry->dwAltNameChoice - 1);
+            CRYPT_EncodeLen(dataLen, pbEncoded, &lenBytes);
+            pbEncoded += lenBytes;
+            switch (entry->dwAltNameChoice)
+            {
+            case CERT_ALT_NAME_RFC822_NAME:
+            case CERT_ALT_NAME_DNS_NAME:
+            case CERT_ALT_NAME_URL:
+            {
+                DWORD i;
+
+                for (i = 0; i < dataLen; i++)
+                    *pbEncoded++ = (BYTE)entry->u.pwszURL[i];
+                break;
+            }
+            case CERT_ALT_NAME_IP_ADDRESS:
+                memcpy(pbEncoded, entry->u.IPAddress.pbData, dataLen);
+                break;
+            }
+            if (ret)
+                *pcbEncoded = bytesNeeded;
+        }
+    }
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnEncodeAltName(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
+ PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
+{
+    BOOL ret;
+
+    __TRY
+    {
+        const CERT_ALT_NAME_INFO *info =
+         (const CERT_ALT_NAME_INFO *)pvStructInfo;
+
+        DWORD bytesNeeded, dataLen, lenBytes, i;
+
+        ret = TRUE;
+        /* FIXME: should check that cAltEntry is not bigger than 0xff, since we
+         * can't encode an erroneous entry index if it's bigger than this.
+         */
+        for (i = 0, dataLen = 0; ret && i < info->cAltEntry; i++)
+        {
+            DWORD len;
+
+            ret = CRYPT_AsnEncodeAltNameEntry(&info->rgAltEntry[i], NULL,
+             &len);
+            if (ret)
+                dataLen += len;
+        }
+        if (ret)
+        {
+            CRYPT_EncodeLen(dataLen, NULL, &lenBytes);
+            bytesNeeded = 1 + lenBytes + dataLen;
+            if (!pbEncoded)
+            {
+                *pcbEncoded = bytesNeeded;
+                ret = TRUE;
+            }
+            else
+            {
+                if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara,
+                 pbEncoded, pcbEncoded, bytesNeeded)))
+                {
+                    if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
+                        pbEncoded = *(BYTE **)pbEncoded;
+                    *pbEncoded++ = ASN_SEQUENCEOF;
+                    CRYPT_EncodeLen(dataLen, pbEncoded, &lenBytes);
+                    pbEncoded += lenBytes;
+                    for (i = 0; ret && i < info->cAltEntry; i++)
+                    {
+                        DWORD len = dataLen;
+
+                        ret = CRYPT_AsnEncodeAltNameEntry(&info->rgAltEntry[i],
+                         pbEncoded, &len);
+                        if (ret)
+                        {
+                            pbEncoded += len;
+                            dataLen -= len;
+                        }
+                        else if (GetLastError() == CRYPT_E_INVALID_IA5_STRING)
+                        {
+                            /* CRYPT_AsnEncodeAltNameEntry encoded the index of
+                             * the bad character, now set the index of the bad
+                             * entry
+                             */
+                            *pcbEncoded |= (BYTE)i <<
+                             CERT_ALT_NAME_ENTRY_ERR_INDEX_SHIFT;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    __EXCEPT(page_fault)
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
 static BOOL WINAPI CRYPT_AsnEncodeBasicConstraints2(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
  PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
@@ -1905,10 +2230,9 @@
     HMODULE lib = NULL;
     CryptEncodeObjectExFunc encodeFunc = NULL;
 
-    TRACE("(0x%08lx, %s, %p, 0x%08lx, %p, %p, %p)\n",
-     dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) :
-     "(integer value)", pvStructInfo, dwFlags, pEncodePara, pvEncoded,
-     pcbEncoded);
+    TRACE("(0x%08lx, %s, %p, 0x%08lx, %p, %p, %p)\n", dwCertEncodingType,
+     debugstr_a(lpszStructType), pvStructInfo, dwFlags, pEncodePara,
+     pvEncoded, pcbEncoded);
 
     if (!pvEncoded && !pcbEncoded)
     {
@@ -1935,6 +2259,9 @@
         case (WORD)X509_CERT_TO_BE_SIGNED:
             encodeFunc = CRYPT_AsnEncodeCertInfo;
             break;
+        case (WORD)X509_CERT_CRL_TO_BE_SIGNED:
+            encodeFunc = CRYPT_AsnEncodeCRLInfo;
+            break;
         case (WORD)X509_EXTENSIONS:
             encodeFunc = CRYPT_AsnEncodeExtensions;
             break;
@@ -1944,6 +2271,9 @@
         case (WORD)X509_PUBLIC_KEY_INFO:
             encodeFunc = CRYPT_AsnEncodePubKeyInfo;
             break;
+        case (WORD)X509_ALTERNATE_NAME:
+            encodeFunc = CRYPT_AsnEncodeAltName;
+            break;
         case (WORD)X509_BASIC_CONSTRAINTS2:
             encodeFunc = CRYPT_AsnEncodeBasicConstraints2;
             break;
@@ -1991,6 +2321,16 @@
         encodeFunc = CRYPT_AsnEncodeOctets;
     else if (!strcmp(lpszStructType, szOID_BASIC_CONSTRAINTS2))
         encodeFunc = CRYPT_AsnEncodeBasicConstraints2;
+    else if (!strcmp(lpszStructType, szOID_ISSUER_ALT_NAME))
+        encodeFunc = CRYPT_AsnEncodeAltName;
+    else if (!strcmp(lpszStructType, szOID_ISSUER_ALT_NAME2))
+        encodeFunc = CRYPT_AsnEncodeAltName;
+    else if (!strcmp(lpszStructType, szOID_NEXT_UPDATE_LOCATION))
+        encodeFunc = CRYPT_AsnEncodeAltName;
+    else if (!strcmp(lpszStructType, szOID_SUBJECT_ALT_NAME))
+        encodeFunc = CRYPT_AsnEncodeAltName;
+    else if (!strcmp(lpszStructType, szOID_SUBJECT_ALT_NAME2))
+        encodeFunc = CRYPT_AsnEncodeAltName;
     else
         TRACE("OID %s not found or unimplemented, looking for DLL\n",
          debugstr_a(lpszStructType));
@@ -2015,10 +2355,9 @@
     HMODULE lib;
     CryptDecodeObjectFunc pCryptDecodeObject;
 
-    TRACE("(0x%08lx, %s, %p, %ld, 0x%08lx, %p, %p)\n",
-     dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) :
-     "(integer value)", pbEncoded, cbEncoded, dwFlags, pvStructInfo,
-     pcbStructInfo);
+    TRACE("(0x%08lx, %s, %p, %ld, 0x%08lx, %p, %p)\n", dwCertEncodingType,
+     debugstr_a(lpszStructType), pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, pcbStructInfo);
 
     if (!pvStructInfo && !pcbStructInfo)
     {
@@ -2142,6 +2481,9 @@
  * offset:
  *     A sequence is decoded into a struct.  The offset member is the
  *     offset of this item within that struct.
+ * decodeFunc:
+ *     The decoder function to use.  If this is NULL, then the member isn't
+ *     decoded, but minSize space is reserved for it.
  * minSize:
  *     The minimum amount of space occupied after decoding.  You must set this.
  * hasPointer, pointerOffset, minSize:
@@ -2165,6 +2507,9 @@
 /* This decodes an arbitrary sequence into a contiguous block of memory
  * (basically, a struct.)  Each element being decoded is described by a struct
  * AsnDecodeSequenceItem, see above.
+ * startingPointer is an optional pointer to the first place where dynamic
+ * data will be stored.  If you know the starting offset, you may pass it
+ * here.  Otherwise, pass NULL, and one will be inferred from the items.
  * Each item decoder is never called with CRYPT_DECODE_ALLOC_FLAG set.
  * If any undecoded data are left over, fails with CRYPT_E_ASN1_CORRUPT.
  * FIXME: use to decode more sequences.
@@ -2172,10 +2517,13 @@
 static BOOL CRYPT_AsnDecodeSequence(DWORD dwCertEncodingType,
  struct AsnDecodeSequenceItem items[], DWORD cItem, const BYTE *pbEncoded,
  DWORD cbEncoded, DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara,
- void *pvStructInfo, DWORD *pcbStructInfo)
+ void *pvStructInfo, DWORD *pcbStructInfo, void *startingPointer)
 {
     BOOL ret;
 
+    TRACE("%p, %ld, %p, %ld, %08lx, %p, %p, %ld\n", items, cItem, pbEncoded,
+     cbEncoded, dwFlags, pDecodePara, pvStructInfo, *pcbStructInfo);
+
     if (pbEncoded[0] == ASN_SEQUENCE)
     {
         DWORD dataLen;
@@ -2199,26 +2547,31 @@
                     {
                         BYTE nextItemLenBytes = GET_LEN_BYTES(ptr[1]);
 
-                        ret = items[i].decodeFunc(dwCertEncodingType, NULL,
-                         ptr, 1 + nextItemLenBytes + nextItemLen,
-                         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL,
-                         &items[i].size);
-                        if (ret)
+                        if (items[i].decodeFunc)
                         {
-                            /* Account for alignment padding */
-                            bytesNeeded += items[i].size;
-                            if (items[i].size % sizeof(DWORD))
-                                bytesNeeded += sizeof(DWORD) -
-                                 items[i].size % sizeof(DWORD);
-                            ptr += 1 + nextItemLenBytes + nextItemLen;
+                            ret = items[i].decodeFunc(dwCertEncodingType, NULL,
+                             ptr, 1 + nextItemLenBytes + nextItemLen,
+                             dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL,
+                             &items[i].size);
+                            if (ret)
+                            {
+                                /* Account for alignment padding */
+                                bytesNeeded += items[i].size;
+                                if (items[i].size % sizeof(DWORD))
+                                    bytesNeeded += sizeof(DWORD) -
+                                     items[i].size % sizeof(DWORD);
+                                ptr += 1 + nextItemLenBytes + nextItemLen;
+                            }
+                            else if (items[i].optional &&
+                             GetLastError() == CRYPT_E_ASN1_BADTAG)
+                            {
+                                bytesNeeded += items[i].minSize;
+                                SetLastError(NOERROR);
+                                ret = TRUE;
+                            }
                         }
-                        else if (items[i].optional &&
-                         GetLastError() == CRYPT_E_ASN1_BADTAG)
-                        {
+                        else
                             bytesNeeded += items[i].minSize;
-                            SetLastError(NOERROR);
-                            ret = TRUE;
-                        }
                     }
                 }
                 else if (items[i].optional)
@@ -2240,7 +2593,10 @@
 
                     if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
                         pvStructInfo = *(BYTE **)pvStructInfo;
-                    nextData = (BYTE *)pvStructInfo + minSize;
+                    if (startingPointer)
+                        nextData = (BYTE *)startingPointer;
+                    else
+                        nextData = (BYTE *)pvStructInfo + minSize;
                     memset(pvStructInfo, 0, minSize);
                     ptr = pbEncoded + 1 + lenBytes;
                     for (i = 0; ret && i < cItem; i++)
@@ -2255,11 +2611,14 @@
                             if (items[i].hasPointer)
                                 *(BYTE **)((BYTE *)pvStructInfo +
                                  items[i].pointerOffset) = nextData;
-                            ret = items[i].decodeFunc(dwCertEncodingType, NULL,
-                             ptr, 1 + nextItemLenBytes + nextItemLen,
-                             dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL,
-                             (BYTE *)pvStructInfo + items[i].offset,
-                             &items[i].size);
+                            if (items[i].decodeFunc)
+                                ret = items[i].decodeFunc(dwCertEncodingType,
+                                 NULL, ptr, 1 + nextItemLenBytes + nextItemLen,
+                                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL,
+                                 (BYTE *)pvStructInfo + items[i].offset,
+                                 &items[i].size);
+                            else
+                                items[i].size = items[i].minSize;
                             if (ret)
                             {
                                 if (items[i].hasPointer &&
@@ -2296,6 +2655,7 @@
         SetLastError(CRYPT_E_ASN1_BADTAG);
         ret = FALSE;
     }
+    TRACE("returning %d (%08lx)\n", ret, GetLastError());
     return ret;
 }
 
@@ -2379,6 +2739,9 @@
 {
     BOOL ret = TRUE;
 
+    TRACE("%p, %ld, %08lx, %p, %p, %ld\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
+
     __TRY
     {
         struct AsnDecodeSequenceItem items[] = {
@@ -2398,7 +2761,7 @@
             items[2].decodeFunc = CRYPT_AsnDecodeBitsInternal;
         ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
          sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
-         pDecodePara, pvStructInfo, pcbStructInfo);
+         pDecodePara, pvStructInfo, pcbStructInfo, NULL);
     }
     __EXCEPT(page_fault)
     {
@@ -2451,7 +2814,7 @@
 
     ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
      sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, pcbStructInfo);
+     pDecodePara, pvStructInfo, pcbStructInfo, NULL);
     return ret;
 }
 
@@ -2461,6 +2824,9 @@
 {
     BOOL ret = TRUE;
 
+    TRACE("%p, %ld, %08lx, %p, %p, %ld\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
+
     __TRY
     {
         struct AsnDecodeSequenceItem items[] = {
@@ -2489,14 +2855,14 @@
          { offsetof(CERT_INFO, SubjectUniqueId), CRYPT_AsnDecodeBitsInternal,
            sizeof(CRYPT_BIT_BLOB), TRUE, TRUE, offsetof(CERT_INFO,
            SubjectUniqueId.pbData), 0 },
-         { offsetof(CERT_INFO, cExtension), CRYPT_AsnDecodeExtensions,
+         { offsetof(CERT_INFO, cExtension), CRYPT_AsnDecodeExtensionsInternal,
            sizeof(CERT_EXTENSIONS), TRUE, TRUE, offsetof(CERT_INFO,
            rgExtension), 0 },
         };
 
         ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
          sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
-         pDecodePara, pvStructInfo, pcbStructInfo);
+         pDecodePara, pvStructInfo, pcbStructInfo, NULL);
     }
     __EXCEPT(page_fault)
     {
@@ -2507,6 +2873,188 @@
     return ret;
 }
 
+static BOOL CRYPT_AsnDecodeCRLEntry(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, PCRL_ENTRY entry, DWORD *pcbEntry)
+{
+    BOOL ret;
+    struct AsnDecodeSequenceItem items[] = {
+     { offsetof(CRL_ENTRY, SerialNumber), CRYPT_AsnDecodeIntegerInternal,
+       sizeof(CRYPT_INTEGER_BLOB), FALSE, TRUE, offsetof(CRL_ENTRY,
+       SerialNumber.pbData), 0 },
+     { offsetof(CRL_ENTRY, RevocationDate), CRYPT_AsnDecodeChoiceOfTime,
+       sizeof(FILETIME), FALSE, FALSE, 0 },
+     { offsetof(CRL_ENTRY, cExtension), CRYPT_AsnDecodeExtensionsInternal,
+       sizeof(CERT_EXTENSIONS), TRUE, TRUE, offsetof(CRL_ENTRY,
+       rgExtension), 0 },
+    };
+
+    TRACE("%p, %ld, %08lx, %p, %ld\n", pbEncoded, cbEncoded, dwFlags, entry,
+     *pcbEntry);
+
+    ret = CRYPT_AsnDecodeSequence(X509_ASN_ENCODING, items,
+     sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
+     NULL, entry, pcbEntry, entry ? entry->SerialNumber.pbData : NULL);
+    TRACE("Returning %d (%08lx)\n", ret, GetLastError());
+    return ret;
+}
+
+typedef struct _WINE_CRL_ENTRIES {
+    DWORD      cCRLEntry;
+    PCRL_ENTRY rgCRLEntry;
+} WINE_CRL_ENTRIES, *PWINE_CRL_ENTRIES;
+
+/* Warning: assumes pvStructInfo is a WINE_CRL_ENTRIES whose rgCRLEntry has
+ * been set prior to calling.
+ */
+static BOOL WINAPI CRYPT_AsnDecodeCRLEntries(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret;
+
+    TRACE("%p, %ld, %08lx, %p, %p, %ld\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
+
+    if (pbEncoded[0] == ASN_SEQUENCEOF)
+    {
+        DWORD dataLen, bytesNeeded;
+
+        if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+        {
+            DWORD cCRLEntry = 0;
+            BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+
+            bytesNeeded = sizeof(WINE_CRL_ENTRIES);
+            if (dataLen)
+            {
+                const BYTE *ptr;
+                DWORD size;
+
+                for (ptr = pbEncoded + 1 + lenBytes; ret &&
+                 ptr - pbEncoded - 1 - lenBytes < dataLen; )
+                {
+                    size = 0;
+                    ret = CRYPT_AsnDecodeCRLEntry(ptr,
+                     cbEncoded - (ptr - pbEncoded), dwFlags, NULL, &size);
+                    if (ret)
+                    {
+                        DWORD nextLen;
+
+                        cCRLEntry++;
+                        bytesNeeded += size;
+                        ret = CRYPT_GetLen(ptr,
+                         cbEncoded - (ptr - pbEncoded), &nextLen);
+                        if (ret)
+                            ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]);
+                    }
+                }
+            }
+            if (ret)
+            {
+                if (!pvStructInfo)
+                    *pcbStructInfo = bytesNeeded;
+                else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags,
+                 pDecodePara, pvStructInfo, pcbStructInfo, bytesNeeded)))
+                {
+                    DWORD size, i;
+                    BYTE *nextData;
+                    const BYTE *ptr;
+                    PWINE_CRL_ENTRIES entries;
+
+                    if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                        pvStructInfo = *(BYTE **)pvStructInfo;
+                    *pcbStructInfo = bytesNeeded;
+                    entries = (PWINE_CRL_ENTRIES)pvStructInfo;
+                    entries->cCRLEntry = cCRLEntry;
+                    assert(entries->rgCRLEntry);
+                    nextData = (BYTE *)entries->rgCRLEntry +
+                     entries->cCRLEntry * sizeof(CRL_ENTRY);
+                    for (i = 0, ptr = pbEncoded + 1 + lenBytes; ret &&
+                     i < cCRLEntry && ptr - pbEncoded - 1 - lenBytes <
+                     dataLen; i++)
+                    {
+                        entries->rgCRLEntry[i].SerialNumber.pbData = nextData;
+                        size = bytesNeeded;
+                        ret = CRYPT_AsnDecodeCRLEntry(ptr,
+                         cbEncoded - (ptr - pbEncoded), dwFlags,
+                         &entries->rgCRLEntry[i], &size);
+                        if (ret)
+                        {
+                            DWORD nextLen;
+
+                            bytesNeeded -= size;
+                            /* Increment nextData by the difference of the
+                             * minimum size and the actual size.
+                             */
+                            if (size > sizeof(CRL_ENTRY))
+                                nextData += size - sizeof(CRL_ENTRY);
+                            ret = CRYPT_GetLen(ptr,
+                             cbEncoded - (ptr - pbEncoded), &nextLen);
+                            if (ret)
+                                ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    else
+    {
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+        ret = FALSE;
+    }
+    TRACE("Returning %d (%08lx)\n", ret, GetLastError());
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeCRLInfo(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret = TRUE;
+
+    TRACE("%p, %ld, %08lx, %p, %p, %ld\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
+
+    __TRY
+    {
+        struct AsnDecodeSequenceItem items[] = {
+         { offsetof(CRL_INFO, dwVersion), CRYPT_AsnDecodeCertVersion,
+           sizeof(DWORD), TRUE, FALSE, 0, 0 },
+         { offsetof(CRL_INFO, SignatureAlgorithm), CRYPT_AsnDecodeAlgorithmId,
+           sizeof(CRYPT_ALGORITHM_IDENTIFIER), FALSE, TRUE, offsetof(CRL_INFO,
+           SignatureAlgorithm.Parameters.pbData), 0 },
+         { offsetof(CRL_INFO, Issuer), CRYPT_AsnDecodeDerBlob,
+           sizeof(CRYPT_DER_BLOB), FALSE, TRUE, offsetof(CRL_INFO,
+           Issuer.pbData) },
+         { offsetof(CRL_INFO, ThisUpdate), CRYPT_AsnDecodeChoiceOfTime,
+           sizeof(FILETIME), FALSE, FALSE, 0 },
+         { offsetof(CRL_INFO, NextUpdate), CRYPT_AsnDecodeChoiceOfTime,
+           sizeof(FILETIME), TRUE, FALSE, 0 },
+         { offsetof(CRL_INFO, cCRLEntry), CRYPT_AsnDecodeCRLEntries,
+           sizeof(WINE_CRL_ENTRIES), TRUE, TRUE, offsetof(CRL_INFO,
+           rgCRLEntry), 0 },
+         /* Note that the extensions are ignored by MS, so I'll ignore them too
+          */
+         { offsetof(CRL_INFO, cExtension), NULL,
+           sizeof(CERT_EXTENSIONS), TRUE, FALSE, 0 },
+        };
+
+        ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
+         sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
+         pDecodePara, pvStructInfo, pcbStructInfo, NULL);
+    }
+    __EXCEPT(page_fault)
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+
+    TRACE("Returning %d (%08lx)\n", ret, GetLastError());
+    return ret;
+}
+
 /* Warning:  assumes ext->Value.pbData is set ahead of time! */
 static BOOL CRYPT_AsnDecodeExtension(const BYTE *pbEncoded, DWORD cbEncoded,
  DWORD dwFlags, CERT_EXTENSION *ext, DWORD *pcbExt)
@@ -2601,6 +3149,115 @@
     return ret;
 }
 
+static BOOL WINAPI CRYPT_AsnDecodeExtensionsInternal(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret = TRUE;
+
+    if (pbEncoded[0] == ASN_SEQUENCEOF)
+    {
+        DWORD dataLen, bytesNeeded;
+
+        if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+        {
+            DWORD cExtension = 0;
+            BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+
+            bytesNeeded = sizeof(CERT_EXTENSIONS);
+            if (dataLen)
+            {
+                const BYTE *ptr;
+                DWORD size;
+
+                for (ptr = pbEncoded + 1 + lenBytes; ret &&
+                 ptr - pbEncoded - 1 - lenBytes < dataLen; )
+                {
+                    ret = CRYPT_AsnDecodeExtension(ptr,
+                     cbEncoded - (ptr - pbEncoded), dwFlags, NULL, &size);
+                    if (ret)
+                    {
+                        DWORD nextLen;
+
+                        cExtension++;
+                        bytesNeeded += size;
+                        ret = CRYPT_GetLen(ptr,
+                         cbEncoded - (ptr - pbEncoded), &nextLen);
+                        if (ret)
+                            ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]);
+                    }
+                }
+            }
+            if (ret)
+            {
+                if (!pvStructInfo)
+                    *pcbStructInfo = bytesNeeded;
+                else if (*pcbStructInfo < bytesNeeded)
+                {
+                    SetLastError(ERROR_MORE_DATA);
+                    *pcbStructInfo = bytesNeeded;
+                    ret = FALSE;
+                }
+                else
+                {
+                    DWORD size, i;
+                    BYTE *nextData;
+                    const BYTE *ptr;
+                    CERT_EXTENSIONS *exts;
+
+                    *pcbStructInfo = bytesNeeded;
+                    exts = (CERT_EXTENSIONS *)pvStructInfo;
+                    exts->cExtension = cExtension;
+                    assert(exts->rgExtension);
+                    nextData = (BYTE *)exts->rgExtension +
+                     exts->cExtension * sizeof(CERT_EXTENSION);
+                    for (i = 0, ptr = pbEncoded + 1 + lenBytes; ret &&
+                     i < cExtension && ptr - pbEncoded - 1 - lenBytes <
+                     dataLen; i++)
+                    {
+                        exts->rgExtension[i].Value.pbData = nextData;
+                        size = bytesNeeded;
+                        ret = CRYPT_AsnDecodeExtension(ptr,
+                         cbEncoded - (ptr - pbEncoded), dwFlags,
+                         &exts->rgExtension[i], &size);
+                        if (ret)
+                        {
+                            DWORD nextLen;
+
+                            bytesNeeded -= size;
+                            /* If dwFlags & CRYPT_DECODE_NOCOPY_FLAG, the
+                             * data may not have been copied.
+                             */
+                            if (exts->rgExtension[i].Value.pbData ==
+                             nextData)
+                                nextData +=
+                                 exts->rgExtension[i].Value.cbData;
+                            /* Ugly: the OID, if copied, is stored in
+                             * memory after the value, so increment by its
+                             * string length if it's set and points here.
+                             */
+                            if ((const BYTE *)exts->rgExtension[i].pszObjId
+                             == nextData)
+                                nextData += strlen(
+                                 exts->rgExtension[i].pszObjId) + 1;
+                            ret = CRYPT_GetLen(ptr,
+                             cbEncoded - (ptr - pbEncoded), &nextLen);
+                            if (ret)
+                                ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    else
+    {
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+        ret = FALSE;
+    }
+    return ret;
+}
+
 static BOOL WINAPI CRYPT_AsnDecodeExtensions(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
@@ -2609,104 +3266,28 @@
 
     __TRY
     {
-        if (pbEncoded[0] == ASN_SEQUENCEOF)
+        ret = CRYPT_AsnDecodeExtensionsInternal(dwCertEncodingType,
+         lpszStructType, pbEncoded, cbEncoded,
+         dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL, pcbStructInfo);
+        if (ret && pvStructInfo)
         {
-            DWORD dataLen, bytesNeeded;
-
-            if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+            ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
+             pcbStructInfo, *pcbStructInfo);
+            if (ret)
             {
-                DWORD cExtension = 0;
-                BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+                CERT_EXTENSIONS *exts;
 
-                bytesNeeded = sizeof(CERT_EXTENSIONS);
-                if (dataLen)
-                {
-                    const BYTE *ptr;
-                    DWORD size;
-
-                    for (ptr = pbEncoded + 1 + lenBytes; ret &&
-                     ptr - pbEncoded - 1 - lenBytes < dataLen; )
-                    {
-                        ret = CRYPT_AsnDecodeExtension(ptr,
-                         cbEncoded - (ptr - pbEncoded), dwFlags, NULL, &size);
-                        if (ret)
-                        {
-                            DWORD nextLen;
-
-                            cExtension++;
-                            bytesNeeded += size;
-                            ret = CRYPT_GetLen(ptr,
-                             cbEncoded - (ptr - pbEncoded), &nextLen);
-                            if (ret)
-                                ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]);
-                        }
-                    }
-                }
-                if (ret)
-                {
-                    if (!pvStructInfo)
-                        *pcbStructInfo = bytesNeeded;
-                    else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags,
-                     pDecodePara, pvStructInfo, pcbStructInfo, bytesNeeded)))
-                    {
-                        DWORD size, i;
-                        BYTE *nextData;
-                        const BYTE *ptr;
-                        CERT_EXTENSIONS *exts;
-
-                        if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
-                            pvStructInfo = *(BYTE **)pvStructInfo;
-                        *pcbStructInfo = bytesNeeded;
-                        exts = (CERT_EXTENSIONS *)pvStructInfo;
-                        exts->cExtension = cExtension;
-                        exts->rgExtension = (CERT_EXTENSION *)((BYTE *)exts +
-                         sizeof(CERT_EXTENSIONS));
-                        nextData = (BYTE *)exts->rgExtension +
-                         exts->cExtension * sizeof(CERT_EXTENSION);
-                        for (i = 0, ptr = pbEncoded + 1 + lenBytes; ret &&
-                         i < cExtension && ptr - pbEncoded - 1 - lenBytes <
-                         dataLen; i++)
-                        {
-                            exts->rgExtension[i].Value.pbData = nextData;
-                            size = bytesNeeded;
-                            ret = CRYPT_AsnDecodeExtension(ptr,
-                             cbEncoded - (ptr - pbEncoded), dwFlags,
-                             &exts->rgExtension[i], &size);
-                            if (ret)
-                            {
-                                DWORD nextLen;
-
-                                bytesNeeded -= size;
-                                /* If dwFlags & CRYPT_DECODE_NOCOPY_FLAG, the
-                                 * data may not have been copied.
-                                 */
-                                if (exts->rgExtension[i].Value.pbData ==
-                                 nextData)
-                                    nextData +=
-                                     exts->rgExtension[i].Value.cbData;
-                                /* Ugly: the OID, if copied, is stored in
-                                 * memory after the value, so increment by its
-                                 * string length if it's set and points here.
-                                 */
-                                if ((const BYTE *)exts->rgExtension[i].pszObjId
-                                 == nextData)
-                                    nextData += strlen(
-                                     exts->rgExtension[i].pszObjId) + 1;
-                                ret = CRYPT_GetLen(ptr,
-                                 cbEncoded - (ptr - pbEncoded), &nextLen);
-                                if (ret)
-                                    ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]);
-                            }
-                        }
-                    }
-                }
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                exts = (CERT_EXTENSIONS *)pvStructInfo;
+                exts->rgExtension = (CERT_EXTENSION *)((BYTE *)exts +
+                 sizeof(CERT_EXTENSIONS));
+                ret = CRYPT_AsnDecodeExtensionsInternal(dwCertEncodingType,
+                 lpszStructType, pbEncoded, cbEncoded,
+                 dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pvStructInfo,
+                 pcbStructInfo);
             }
         }
-        else
-        {
-            SetLastError(CRYPT_E_ASN1_BADTAG);
-            ret = FALSE;
-        }
     }
     __EXCEPT(page_fault)
     {
@@ -3410,7 +3991,7 @@
 
         ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
          sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
-         pDecodePara, pvStructInfo, pcbStructInfo);
+         pDecodePara, pvStructInfo, pcbStructInfo, NULL);
     }
     __EXCEPT(page_fault)
     {
@@ -3466,6 +4047,211 @@
     return ret;
 }
 
+static BOOL CRYPT_AsnDecodeAltNameEntry(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, CERT_ALT_NAME_ENTRY *entry, DWORD *pcbEntry)
+{
+    DWORD dataLen, lenBytes, bytesNeeded = sizeof(CERT_ALT_NAME_ENTRY);
+    BOOL ret;
+
+    if (cbEncoded < 2)
+    {
+        SetLastError(CRYPT_E_ASN1_CORRUPT);
+        return FALSE;
+    }
+    if ((pbEncoded[0] & ASN_FLAGS_MASK) != ASN_CONTEXT)
+    {
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+        return FALSE;
+    }
+    lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+    if (1 + lenBytes > cbEncoded)
+    {
+        SetLastError(CRYPT_E_ASN1_CORRUPT);
+        return FALSE;
+    }
+    if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+    {
+        switch (pbEncoded[0] & ASN_TYPE_MASK)
+        {
+        case 1: /* rfc822Name */
+        case 2: /* dNSName */
+        case 6: /* uniformResourceIdentifier */
+            bytesNeeded += (dataLen + 1) * sizeof(WCHAR);
+            break;
+        case 7: /* iPAddress */
+            bytesNeeded += dataLen;
+            break;
+        case 8: /* registeredID */
+            /* FIXME: decode as OID */
+        case 0: /* otherName */
+        case 4: /* directoryName */
+            FIXME("stub\n");
+            SetLastError(CRYPT_E_ASN1_BADTAG);
+            ret = FALSE;
+            break;
+        case 3: /* x400Address, unimplemented */
+        case 5: /* ediPartyName, unimplemented */
+            SetLastError(CRYPT_E_ASN1_BADTAG);
+            ret = FALSE;
+            break;
+        default:
+            SetLastError(CRYPT_E_ASN1_CORRUPT);
+            ret = FALSE;
+        }
+        if (ret)
+        {
+            if (!entry)
+                *pcbEntry = bytesNeeded;
+            else if (*pcbEntry < bytesNeeded)
+            {
+                SetLastError(ERROR_MORE_DATA);
+                ret = FALSE;
+            }
+            else
+            {
+                /* MS used values one greater than the asn1 ones.. sigh */
+                entry->dwAltNameChoice = (pbEncoded[0] & 0x7f) + 1;
+                switch (pbEncoded[0] & ASN_TYPE_MASK)
+                {
+                case 1: /* rfc822Name */
+                case 2: /* dNSName */
+                case 6: /* uniformResourceIdentifier */
+                {
+                    DWORD i;
+
+                    for (i = 0; i < dataLen; i++)
+                        entry->u.pwszURL[i] = (WCHAR)pbEncoded[1 + lenBytes + i];
+                    entry->u.pwszURL[i] = 0;
+                    break;
+                }
+                case 7: /* iPAddress */
+                    /* The next data pointer is in the pwszURL spot, that is,
+                     * the first 4 bytes.  Need to move it to the next spot.
+                     */
+                    entry->u.IPAddress.pbData = (LPBYTE)entry->u.pwszURL;
+                    entry->u.IPAddress.cbData = dataLen;
+                    memcpy(entry->u.IPAddress.pbData, pbEncoded + 1 + lenBytes,
+                     dataLen);
+                    break;
+                }
+            }
+        }
+    }
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeAltName(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret = TRUE;
+
+    __TRY
+    {
+        if (pbEncoded[0] == ASN_SEQUENCEOF)
+        {
+            DWORD dataLen;
+
+            if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+            {
+                DWORD bytesNeeded, cEntry = 0;
+                BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+
+                bytesNeeded = sizeof(CERT_ALT_NAME_INFO);
+                if (dataLen)
+                {
+                    const BYTE *ptr;
+
+                    for (ptr = pbEncoded + 1 + lenBytes; ret &&
+                     ptr - pbEncoded - 1 - lenBytes < dataLen; )
+                    {
+                        DWORD size;
+
+                        ret = CRYPT_AsnDecodeAltNameEntry(ptr,
+                         cbEncoded - (ptr - pbEncoded), dwFlags, NULL, &size);
+                        if (ret)
+                        {
+                            DWORD nextLen;
+
+                            cEntry++;
+                            bytesNeeded += size;
+                            ret = CRYPT_GetLen(ptr,
+                             cbEncoded - (ptr - pbEncoded), &nextLen);
+                            if (ret)
+                                ptr += nextLen + 1 + GET_LEN_BYTES(ptr[1]);
+                        }
+                    }
+                }
+                if (ret)
+                {
+                    if (!pvStructInfo)
+                        *pcbStructInfo = bytesNeeded;
+                    else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags,
+                     pDecodePara, pvStructInfo, pcbStructInfo, bytesNeeded)))
+                    {
+                        CERT_ALT_NAME_INFO *info;
+
+                        if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                            pvStructInfo = *(BYTE **)pvStructInfo;
+                        info = (CERT_ALT_NAME_INFO *)pvStructInfo;
+                        info->cAltEntry = 0;
+                        if (cEntry == 0)
+                            info->rgAltEntry = NULL;
+                        else
+                        {
+                            DWORD size, i;
+                            BYTE *nextData;
+                            const BYTE *ptr;
+
+                            info->rgAltEntry =
+                             (CERT_ALT_NAME_ENTRY *)((BYTE *)pvStructInfo +
+                             sizeof(CERT_ALT_NAME_INFO));
+                            nextData = (BYTE *)info->rgAltEntry +
+                             cEntry * sizeof(CERT_ALT_NAME_ENTRY);
+                            for (i = 0, ptr = pbEncoded + 1 + lenBytes; ret &&
+                             i < cEntry && ptr - pbEncoded - 1 - lenBytes <
+                             dataLen; i++)
+                            {
+                                info->rgAltEntry[i].u.pwszURL = (LPWSTR)nextData;
+                                size = bytesNeeded;
+                                ret = CRYPT_AsnDecodeAltNameEntry(ptr,
+                                 cbEncoded - (ptr - pbEncoded), dwFlags,
+                                 &info->rgAltEntry[i], &size);
+                                if (ret)
+                                {
+                                    DWORD nextLen;
+
+                                    info->cAltEntry++;
+                                    nextData += size -
+                                     sizeof(CERT_ALT_NAME_ENTRY);
+                                    bytesNeeded -= size;
+                                    ret = CRYPT_GetLen(ptr,
+                                     cbEncoded - (ptr - pbEncoded), &nextLen);
+                                    if (ret)
+                                        ptr += nextLen + 1 +
+                                         GET_LEN_BYTES(ptr[1]);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        else
+        {
+            SetLastError(CRYPT_E_ASN1_BADTAG);
+            ret = FALSE;
+        }
+    }
+    __EXCEPT(page_fault)
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
 static BOOL WINAPI CRYPT_AsnDecodeBasicConstraints2(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
@@ -3634,8 +4420,8 @@
 {
     BOOL ret;
 
-    TRACE("(%p, %ld, 0x%08lx, %p, %p, %p)\n", pbEncoded, cbEncoded, dwFlags,
-     pDecodePara, pvStructInfo, pcbStructInfo);
+    TRACE("(%p, %ld, 0x%08lx, %p, %p, %ld)\n", pbEncoded, cbEncoded, dwFlags,
+     pDecodePara, pvStructInfo, *pcbStructInfo);
 
     if (pbEncoded[0] == ASN_BITSTRING)
     {
@@ -3825,8 +4611,10 @@
                     DWORD i;
 
                     for (i = 0; i < blob->cbData; i++)
+                    {
                         blob->pbData[i] = *(pbEncoded + 1 + lenBytes +
                          dataLen - i - 1);
+                    }
                 }
             }
         }
@@ -4287,12 +5075,6 @@
 {
     BOOL ret;
 
-    if (!pvStructInfo)
-    {
-        *pcbStructInfo = sizeof(FILETIME);
-        return TRUE;
-    }
-
     __TRY
     {
         if (pbEncoded[0] == ASN_UTCTIME)
@@ -4430,9 +5212,8 @@
     CryptDecodeObjectExFunc decodeFunc = NULL;
 
     TRACE("(0x%08lx, %s, %p, %ld, 0x%08lx, %p, %p, %p)\n",
-     dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) :
-     "(integer value)", pbEncoded, cbEncoded, dwFlags, pDecodePara,
-     pvStructInfo, pcbStructInfo);
+     dwCertEncodingType, debugstr_a(lpszStructType), pbEncoded,
+     cbEncoded, dwFlags, pDecodePara, pvStructInfo, pcbStructInfo);
 
     if (!pvStructInfo && !pcbStructInfo)
     {
@@ -4469,6 +5250,9 @@
         case (WORD)X509_CERT_TO_BE_SIGNED:
             decodeFunc = CRYPT_AsnDecodeCertInfo;
             break;
+        case (WORD)X509_CERT_CRL_TO_BE_SIGNED:
+            decodeFunc = CRYPT_AsnDecodeCRLInfo;
+            break;
         case (WORD)X509_EXTENSIONS:
             decodeFunc = CRYPT_AsnDecodeExtensions;
             break;
@@ -4478,6 +5262,9 @@
         case (WORD)X509_PUBLIC_KEY_INFO:
             decodeFunc = CRYPT_AsnDecodePubKeyInfo;
             break;
+        case (WORD)X509_ALTERNATE_NAME:
+            decodeFunc = CRYPT_AsnDecodeAltName;
+            break;
         case (WORD)X509_BASIC_CONSTRAINTS2:
             decodeFunc = CRYPT_AsnDecodeBasicConstraints2;
             break;
@@ -4525,6 +5312,16 @@
         decodeFunc = CRYPT_AsnDecodeOctets;
     else if (!strcmp(lpszStructType, szOID_BASIC_CONSTRAINTS2))
         decodeFunc = CRYPT_AsnDecodeBasicConstraints2;
+    else if (!strcmp(lpszStructType, szOID_ISSUER_ALT_NAME))
+        decodeFunc = CRYPT_AsnDecodeAltName;
+    else if (!strcmp(lpszStructType, szOID_ISSUER_ALT_NAME2))
+        decodeFunc = CRYPT_AsnDecodeAltName;
+    else if (!strcmp(lpszStructType, szOID_NEXT_UPDATE_LOCATION))
+        decodeFunc = CRYPT_AsnDecodeAltName;
+    else if (!strcmp(lpszStructType, szOID_SUBJECT_ALT_NAME))
+        decodeFunc = CRYPT_AsnDecodeAltName;
+    else if (!strcmp(lpszStructType, szOID_SUBJECT_ALT_NAME2))
+        decodeFunc = CRYPT_AsnDecodeAltName;
     else
         TRACE("OID %s not found or unimplemented, looking for DLL\n",
          debugstr_a(lpszStructType));
diff --git a/dlls/crypt32/tests/encode.c b/dlls/crypt32/tests/encode.c
index bb67705..daa014f 100644
--- a/dlls/crypt32/tests/encode.c
+++ b/dlls/crypt32/tests/encode.c
@@ -833,6 +833,229 @@
     }
 }
 
+static const BYTE emptyAltName[] = { 0x30, 0x00 };
+static const BYTE emptyURL[] = { 0x30, 0x02, 0x86, 0x00 };
+static const WCHAR url[] = { 'h','t','t','p',':','/','/','w','i','n','e',
+ 'h','q','.','o','r','g',0 };
+static const BYTE encodedURL[] = { 0x30, 0x13, 0x86, 0x11, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x69, 0x6e, 0x65, 0x68, 0x71, 0x2e,
+ 0x6f, 0x72, 0x67 };
+static const WCHAR dnsName[] = { 'w','i','n','e','h','q','.','o','r','g',0 };
+static const BYTE encodedDnsName[] = { 0x30, 0x0c, 0x82, 0x0a, 0x77, 0x69,
+ 0x6e, 0x65, 0x68, 0x71, 0x2e, 0x6f, 0x72, 0x67 };
+static const BYTE localhost[] = { 127, 0, 0, 1 };
+static const BYTE encodedIPAddr[] = { 0x30, 0x06, 0x87, 0x04, 0x7f, 0x00, 0x00,
+ 0x01 };
+
+static void printBytes(const char *hdr, const BYTE *pb, size_t cb)
+{
+    size_t i;
+
+    printf("%s:\n", hdr);
+    for (i = 0; i < cb; i++)
+        printf("%02x ", pb[i]);
+    putchar('\n');
+}
+
+static void test_encodeAltName(DWORD dwEncoding)
+{
+    static const WCHAR nihongo[] = { 0x226f, 0x575b, 0 };
+    CERT_ALT_NAME_INFO info = { 0 };
+    CERT_ALT_NAME_ENTRY entry = { 0 };
+    BYTE *buf = NULL;
+    DWORD size = 0;
+    BOOL ret;
+
+    /* Test with empty info */
+    ret = CryptEncodeObjectEx(dwEncoding, X509_ALTERNATE_NAME, &info,
+     CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    if (buf)
+    {
+        ok(size == sizeof(emptyAltName), "Expected size %d, got %ld\n",
+         sizeof(emptyAltName), size);
+        ok(!memcmp(buf, emptyAltName, size), "Unexpected value\n");
+        LocalFree(buf);
+    }
+    /* Test with an empty entry */
+    info.cAltEntry = 1;
+    info.rgAltEntry = &entry;
+    ret = CryptEncodeObjectEx(dwEncoding, X509_ALTERNATE_NAME, &info,
+     CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
+     "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n",
+     GetLastError());
+    /* Test with an empty pointer */
+    entry.dwAltNameChoice = CERT_ALT_NAME_URL;
+    ret = CryptEncodeObjectEx(dwEncoding, X509_ALTERNATE_NAME, &info,
+     CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    if (buf)
+    {
+        ok(size == sizeof(emptyURL), "Expected size %d, got %ld\n",
+         sizeof(emptyURL), size);
+        ok(!memcmp(buf, emptyURL, size), "Unexpected value\n");
+        LocalFree(buf);
+    }
+    /* Test with a real URL */
+    U(entry).pwszURL = (LPWSTR)url;
+    ret = CryptEncodeObjectEx(dwEncoding, X509_ALTERNATE_NAME, &info,
+     CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    if (buf)
+    {
+        ok(size == sizeof(encodedURL), "Expected size %d, got %ld\n",
+         sizeof(encodedURL), size);
+        ok(!memcmp(buf, encodedURL, size), "Unexpected value\n");
+        LocalFree(buf);
+    }
+    /* Now with the URL containing an invalid IA5 char */
+    U(entry).pwszURL = (LPWSTR)nihongo;
+    ret = CryptEncodeObjectEx(dwEncoding, X509_ALTERNATE_NAME, &info,
+     CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    ok(!ret && GetLastError() == CRYPT_E_INVALID_IA5_STRING,
+     "Expected CRYPT_E_INVALID_IA5_STRING, got %08lx\n", GetLastError());
+    /* Now with the URL missing a scheme */
+    U(entry).pwszURL = (LPWSTR)dnsName;
+    ret = CryptEncodeObjectEx(dwEncoding, X509_ALTERNATE_NAME, &info,
+     CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
+    if (buf)
+    {
+        /* This succeeds, but it shouldn't, so don't worry about conforming */
+        LocalFree(buf);
+    }
+    /* Now with a DNS name */
+    entry.dwAltNameChoice = CERT_ALT_NAME_DNS_NAME;
+    ret = CryptEncodeObjectEx(dwEncoding, X509_ALTERNATE_NAME, &info,
+     CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
+    if (buf)
+    {
+        ok(size == sizeof(encodedDnsName), "Expected size %d, got %ld\n",
+         sizeof(encodedDnsName), size);
+        ok(!memcmp(buf, encodedDnsName, size), "Unexpected value\n");
+        LocalFree(buf);
+    }
+    /* Test with an IP address */
+    entry.dwAltNameChoice = CERT_ALT_NAME_IP_ADDRESS;
+    U(entry).IPAddress.cbData = sizeof(localhost);
+    U(entry).IPAddress.pbData = (LPBYTE)localhost;
+    ret = CryptEncodeObjectEx(dwEncoding, X509_ALTERNATE_NAME, &info,
+     CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    if (buf)
+    {
+        ok(size == sizeof(encodedIPAddr), "Expected size %d, got %ld\n",
+         sizeof(encodedIPAddr), size);
+        ok(!memcmp(buf, encodedIPAddr, size), "Unexpected value\n");
+        LocalFree(buf);
+    }
+}
+
+static void test_decodeAltName(DWORD dwEncoding)
+{
+    static const BYTE unimplementedType[] = { 0x30, 0x06, 0x85, 0x04, 0x7f,
+     0x00, 0x00, 0x01 };
+    static const BYTE bogusType[] = { 0x30, 0x06, 0x89, 0x04, 0x7f, 0x00, 0x00,
+     0x01 };
+    BOOL ret;
+    BYTE *buf = NULL;
+    DWORD bufSize = 0;
+    CERT_ALT_NAME_INFO *info;
+
+    /* Test some bogus ones first */
+    ret = CryptDecodeObjectEx(dwEncoding, X509_ALTERNATE_NAME,
+     unimplementedType, sizeof(unimplementedType), CRYPT_DECODE_ALLOC_FLAG,
+     NULL, (BYTE *)&buf, &bufSize);
+    ok(!ret && GetLastError() == CRYPT_E_ASN1_BADTAG,
+     "Expected CRYPT_E_ASN1_BADTAG, got %08lx\n", GetLastError());
+    ret = CryptDecodeObjectEx(dwEncoding, X509_ALTERNATE_NAME,
+     bogusType, sizeof(bogusType), CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf,
+     &bufSize);
+    ok(!ret && GetLastError() == CRYPT_E_ASN1_CORRUPT,
+     "Expected CRYPT_E_ASN1_CORRUPT, got %08lx\n", GetLastError());
+    /* Now expected cases */
+    ret = CryptDecodeObjectEx(dwEncoding, X509_ALTERNATE_NAME, emptyAltName,
+     emptyAltName[1] + 2, CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf,
+     &bufSize);
+    ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
+    if (buf)
+    {
+        info = (CERT_ALT_NAME_INFO *)buf;
+
+        ok(info->cAltEntry == 0, "Expected 0 entries, got %ld\n",
+         info->cAltEntry);
+        LocalFree(buf);
+    }
+    ret = CryptDecodeObjectEx(dwEncoding, X509_ALTERNATE_NAME, emptyURL,
+     emptyURL[1] + 2, CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf,
+     &bufSize);
+    ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
+    if (buf)
+    {
+        info = (CERT_ALT_NAME_INFO *)buf;
+
+        ok(info->cAltEntry == 1, "Expected 1 entries, got %ld\n",
+         info->cAltEntry);
+        ok(info->rgAltEntry[0].dwAltNameChoice == CERT_ALT_NAME_URL,
+         "Expected CERT_ALT_NAME_URL, got %ld\n",
+         info->rgAltEntry[0].dwAltNameChoice);
+        ok(U(info->rgAltEntry[0]).pwszURL == NULL || !*U(info->rgAltEntry[0]).pwszURL,
+         "Expected empty URL\n");
+        LocalFree(buf);
+    }
+    ret = CryptDecodeObjectEx(dwEncoding, X509_ALTERNATE_NAME, encodedURL,
+     encodedURL[1] + 2, CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf,
+     &bufSize);
+    ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
+    if (buf)
+    {
+        info = (CERT_ALT_NAME_INFO *)buf;
+
+        ok(info->cAltEntry == 1, "Expected 1 entries, got %ld\n",
+         info->cAltEntry);
+        ok(info->rgAltEntry[0].dwAltNameChoice == CERT_ALT_NAME_URL,
+         "Expected CERT_ALT_NAME_URL, got %ld\n",
+         info->rgAltEntry[0].dwAltNameChoice);
+        ok(!lstrcmpW(U(info->rgAltEntry[0]).pwszURL, url), "Unexpected URL\n");
+        LocalFree(buf);
+    }
+    ret = CryptDecodeObjectEx(dwEncoding, X509_ALTERNATE_NAME, encodedDnsName,
+     encodedDnsName[1] + 2, CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf,
+     &bufSize);
+    ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
+    if (buf)
+    {
+        info = (CERT_ALT_NAME_INFO *)buf;
+
+        ok(info->cAltEntry == 1, "Expected 1 entries, got %ld\n",
+         info->cAltEntry);
+        ok(info->rgAltEntry[0].dwAltNameChoice == CERT_ALT_NAME_DNS_NAME,
+         "Expected CERT_ALT_NAME_DNS_NAME, got %ld\n",
+         info->rgAltEntry[0].dwAltNameChoice);
+        ok(!lstrcmpW(U(info->rgAltEntry[0]).pwszDNSName, dnsName),
+         "Unexpected DNS name\n");
+        LocalFree(buf);
+    }
+    ret = CryptDecodeObjectEx(dwEncoding, X509_ALTERNATE_NAME, encodedIPAddr,
+     encodedIPAddr[1] + 2, CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf,
+     &bufSize);
+    ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
+    if (buf)
+    {
+        info = (CERT_ALT_NAME_INFO *)buf;
+
+        ok(info->cAltEntry == 1, "Expected 1 entries, got %ld\n",
+         info->cAltEntry);
+        ok(info->rgAltEntry[0].dwAltNameChoice == CERT_ALT_NAME_IP_ADDRESS,
+         "Expected CERT_ALT_NAME_IP_ADDRESS, got %ld\n",
+         info->rgAltEntry[0].dwAltNameChoice);
+        ok(U(info->rgAltEntry[0]).IPAddress.cbData == sizeof(localhost),
+         "Unexpected IP address length %ld\n",
+          U(info->rgAltEntry[0]).IPAddress.cbData);
+        ok(!memcmp(U(info->rgAltEntry[0]).IPAddress.pbData, localhost,
+         sizeof(localhost)), "Unexpected IP address value\n");
+        LocalFree(buf);
+    }
+}
+
 struct encodedOctets
 {
     const BYTE *val;
@@ -1589,13 +1812,16 @@
     /* The following certs all fail with CRYPT_E_ASN1_CORRUPT, because at a
      * minimum a cert must have a non-zero serial number, an issuer, and a
      * subject.
+     * It's hard to match the errors precisely sometimes, so accept one
+     * that only wine gives as long as it fails
      */
     for (i = 0; i < sizeof(corruptCerts) / sizeof(corruptCerts[0]); i++)
     {
         ret = CryptDecodeObjectEx(dwEncoding, X509_CERT_TO_BE_SIGNED,
          corruptCerts[i], corruptCerts[i][1] + 2, CRYPT_DECODE_ALLOC_FLAG, NULL,
          (BYTE *)&buf, &size);
-        ok(!ret && GetLastError() == CRYPT_E_ASN1_CORRUPT,
+        ok(!ret && (GetLastError() == CRYPT_E_ASN1_CORRUPT ||
+         GetLastError() == CRYPT_E_ASN1_BADTAG),
          "Expected CRYPT_E_ASN1_CORRUPT, got %08lx\n", GetLastError());
     }
     /* Now check with serial number, subject and issuer specified */
@@ -1695,6 +1921,244 @@
     }
 }
 
+static const BYTE v1CRL[] = { 0x30, 0x15, 0x30, 0x02, 0x06, 0x00, 0x18, 0x0f,
+ 0x31, 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x5a };
+static const BYTE v2CRL[] = { 0x30, 0x18, 0x02, 0x01, 0x01, 0x30, 0x02, 0x06,
+ 0x00, 0x18, 0x0f, 0x31, 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x5a };
+static const BYTE v1CRLWithIssuer[] = { 0x30, 0x2c, 0x30, 0x02, 0x06, 0x00,
+ 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0a,
+ 0x4a, 0x75, 0x61, 0x6e, 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x00, 0x18, 0x0f, 0x31,
+ 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x5a };
+static const BYTE v1CRLWithIssuerAndEmptyEntry[] = { 0x30, 0x43, 0x30, 0x02,
+ 0x06, 0x00, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x0a, 0x4a, 0x75, 0x61, 0x6e, 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x00, 0x18,
+ 0x0f, 0x31, 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x30, 0x15, 0x30, 0x13, 0x02, 0x00, 0x18, 0x0f, 0x31, 0x36,
+ 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a };
+static const BYTE v1CRLWithIssuerAndEntry[] = { 0x30, 0x44, 0x30, 0x02, 0x06,
+ 0x00, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x0a, 0x4a, 0x75, 0x61, 0x6e, 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x00, 0x18, 0x0f,
+ 0x31, 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x5a, 0x30, 0x16, 0x30, 0x14, 0x02, 0x01, 0x01, 0x18, 0x0f, 0x31, 0x36,
+ 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a };
+static const BYTE v1CRLWithExt[] = { 0x30, 0x5a, 0x30, 0x02, 0x06, 0x00, 0x30,
+ 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0a, 0x4a,
+ 0x75, 0x61, 0x6e, 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x00, 0x18, 0x0f, 0x31, 0x36,
+ 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a,
+ 0x30, 0x2c, 0x30, 0x2a, 0x02, 0x01, 0x01, 0x18, 0x0f, 0x31, 0x36, 0x30, 0x31,
+ 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x14,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30,
+ 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01 };
+
+static void test_encodeCRLToBeSigned(DWORD dwEncoding)
+{
+    BOOL ret;
+    BYTE *buf = NULL;
+    DWORD size = 0;
+    CRL_INFO info = { 0 };
+    CRL_ENTRY entry = { { 0 }, { 0 }, 0, 0 };
+
+    /* Test with a V1 CRL */
+    ret = CryptEncodeObjectEx(dwEncoding, X509_CERT_CRL_TO_BE_SIGNED, &info,
+     CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
+    if (buf)
+    {
+        ok(size == sizeof(v1CRL), "Expected size %d, got %ld\n",
+         sizeof(v1CRL), size);
+        ok(!memcmp(buf, v1CRL, size), "Got unexpected value\n");
+        LocalFree(buf);
+    }
+    /* Test v2 CRL */
+    info.dwVersion = CRL_V2;
+    ret = CryptEncodeObjectEx(dwEncoding, X509_CERT_CRL_TO_BE_SIGNED, &info,
+     CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
+    if (buf)
+    {
+        ok(size == v2CRL[1] + 2, "Expected size %d, got %ld\n",
+         v2CRL[1] + 2, size);
+        ok(!memcmp(buf, v2CRL, size), "Got unexpected value\n");
+        LocalFree(buf);
+    }
+    /* v1 CRL with a name */
+    info.dwVersion = CRL_V1;
+    info.Issuer.cbData = sizeof(encodedCommonName);
+    info.Issuer.pbData = (BYTE *)encodedCommonName;
+    ret = CryptEncodeObjectEx(dwEncoding, X509_CERT_CRL_TO_BE_SIGNED, &info,
+     CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
+    if (buf)
+    {
+        ok(size == sizeof(v1CRLWithIssuer), "Expected size %d, got %ld\n",
+         sizeof(v1CRLWithIssuer), size);
+        ok(!memcmp(buf, v1CRLWithIssuer, size), "Got unexpected value\n");
+        LocalFree(buf);
+    }
+    /* v1 CRL with a name and a NULL entry pointer */
+    info.cCRLEntry = 1;
+    ret = CryptEncodeObjectEx(dwEncoding, X509_CERT_CRL_TO_BE_SIGNED, &info,
+     CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    ok(!ret && GetLastError() == STATUS_ACCESS_VIOLATION,
+     "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", GetLastError());
+    /* now set an empty entry */
+    info.rgCRLEntry = &entry;
+    ret = CryptEncodeObjectEx(dwEncoding, X509_CERT_CRL_TO_BE_SIGNED, &info,
+     CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    if (buf)
+    {
+        ok(size == sizeof(v1CRLWithIssuerAndEmptyEntry),
+         "Expected size %d, got %ld\n", sizeof(v1CRLWithIssuerAndEmptyEntry),
+         size);
+        ok(!memcmp(buf, v1CRLWithIssuerAndEmptyEntry, size),
+         "Got unexpected value\n");
+        LocalFree(buf);
+    }
+    /* an entry with a serial number */
+    entry.SerialNumber.cbData = sizeof(serialNum);
+    entry.SerialNumber.pbData = (BYTE *)serialNum;
+    ret = CryptEncodeObjectEx(dwEncoding, X509_CERT_CRL_TO_BE_SIGNED, &info,
+     CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    if (buf)
+    {
+        ok(size == sizeof(v1CRLWithIssuerAndEntry),
+         "Expected size %d, got %ld\n", sizeof(v1CRLWithIssuerAndEntry), size);
+        ok(!memcmp(buf, v1CRLWithIssuerAndEntry, size),
+         "Got unexpected value\n");
+        LocalFree(buf);
+    }
+    /* and finally, an entry with an extension */
+    entry.cExtension = 1;
+    entry.rgExtension = &criticalExt;
+    ret = CryptEncodeObjectEx(dwEncoding, X509_CERT_CRL_TO_BE_SIGNED, &info,
+     CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
+    if (buf)
+    {
+        ok(size == sizeof(v1CRLWithExt), "Expected size %d, got %ld\n",
+         sizeof(v1CRLWithExt), size);
+        ok(!memcmp(buf, v1CRLWithExt, size), "Got unexpected value\n");
+        LocalFree(buf);
+    }
+}
+
+static void test_decodeCRLToBeSigned(DWORD dwEncoding)
+{
+    static const BYTE *corruptCRLs[] = { v1CRL, v2CRL };
+    BOOL ret;
+    BYTE *buf = NULL;
+    DWORD size = 0, i;
+
+    for (i = 0; i < sizeof(corruptCRLs) / sizeof(corruptCRLs[0]); i++)
+    {
+        ret = CryptDecodeObjectEx(dwEncoding, X509_CERT_CRL_TO_BE_SIGNED,
+         corruptCRLs[i], corruptCRLs[i][1] + 2, CRYPT_DECODE_ALLOC_FLAG, NULL,
+         (BYTE *)&buf, &size);
+        /* It's hard to match the errors precisely sometimes, so accept one
+         * that only wine gives as long as it fails
+         */
+        ok(!ret && (GetLastError() == CRYPT_E_ASN1_CORRUPT ||
+         GetLastError() == CRYPT_E_ASN1_BADTAG),
+         "Expected CRYPT_E_ASN1_CORRUPT, got %08lx\n", GetLastError());
+    }
+    /* at a minimum, a CRL must contain an issuer: */
+    ret = CryptDecodeObjectEx(dwEncoding, X509_CERT_CRL_TO_BE_SIGNED,
+     v1CRLWithIssuer, v1CRLWithIssuer[1] + 2, CRYPT_DECODE_ALLOC_FLAG, NULL,
+     (BYTE *)&buf, &size);
+    ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
+    if (buf)
+    {
+        CRL_INFO *info = (CRL_INFO *)buf;
+
+        ok(size >= sizeof(CRL_INFO), "Expected size at least %d, got %ld\n",
+         sizeof(CRL_INFO), size);
+        ok(info->cCRLEntry == 0, "Expected 0 CRL entries, got %ld\n",
+         info->cCRLEntry);
+        ok(info->Issuer.cbData == sizeof(encodedCommonName),
+         "Expected issuer of %d bytes, got %ld\n", sizeof(encodedCommonName),
+         info->Issuer.cbData);
+        ok(!memcmp(info->Issuer.pbData, encodedCommonName, info->Issuer.cbData),
+         "Unexpected issuer\n");
+        LocalFree(buf);
+    }
+    /* check decoding with an empty CRL entry */
+    ret = CryptDecodeObjectEx(dwEncoding, X509_CERT_CRL_TO_BE_SIGNED,
+     v1CRLWithIssuerAndEmptyEntry, v1CRLWithIssuerAndEmptyEntry[1] + 2,
+     CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    todo_wine ok(!ret && GetLastError() == CRYPT_E_ASN1_CORRUPT,
+     "Expected CRYPT_E_ASN1_CORRUPT, got %08lx\n", GetLastError());
+    /* with a real CRL entry */
+    ret = CryptDecodeObjectEx(dwEncoding, X509_CERT_CRL_TO_BE_SIGNED,
+     v1CRLWithIssuerAndEntry, v1CRLWithIssuerAndEntry[1] + 2,
+     CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
+    ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
+    if (buf)
+    {
+        CRL_INFO *info = (CRL_INFO *)buf;
+        CRL_ENTRY *entry;
+
+        ok(size >= sizeof(CRL_INFO), "Expected size at least %d, got %ld\n",
+         sizeof(CRL_INFO), size);
+        ok(info->cCRLEntry == 1, "Expected 1 CRL entries, got %ld\n",
+         info->cCRLEntry);
+        ok(info->rgCRLEntry != NULL, "Expected a valid CRL entry array\n");
+        entry = info->rgCRLEntry;
+        ok(entry->SerialNumber.cbData == 1,
+         "Expected serial number size 1, got %ld\n",
+         entry->SerialNumber.cbData);
+        ok(*entry->SerialNumber.pbData == *serialNum,
+         "Expected serial number %d, got %d\n", *serialNum,
+         *entry->SerialNumber.pbData);
+        ok(info->Issuer.cbData == sizeof(encodedCommonName),
+         "Expected issuer of %d bytes, got %ld\n", sizeof(encodedCommonName),
+         info->Issuer.cbData);
+        ok(!memcmp(info->Issuer.pbData, encodedCommonName, info->Issuer.cbData),
+         "Unexpected issuer\n");
+        if (memcmp(info->Issuer.pbData, encodedCommonName, info->Issuer.cbData))
+        {
+            printBytes("Expected", encodedCommonName,
+             sizeof(encodedCommonName));
+            printBytes("Got", info->Issuer.pbData, info->Issuer.cbData);
+        }
+    }
+    /* and finally, with an extension */
+    ret = CryptDecodeObjectEx(dwEncoding, X509_CERT_CRL_TO_BE_SIGNED,
+     v1CRLWithExt, sizeof(v1CRLWithExt), CRYPT_DECODE_ALLOC_FLAG,
+     NULL, (BYTE *)&buf, &size);
+    ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
+    if (buf)
+    {
+        CRL_INFO *info = (CRL_INFO *)buf;
+        CRL_ENTRY *entry;
+
+        ok(size >= sizeof(CRL_INFO), "Expected size at least %d, got %ld\n",
+         sizeof(CRL_INFO), size);
+        ok(info->cCRLEntry == 1, "Expected 1 CRL entries, got %ld\n",
+         info->cCRLEntry);
+        ok(info->rgCRLEntry != NULL, "Expected a valid CRL entry array\n");
+        entry = info->rgCRLEntry;
+        ok(entry->SerialNumber.cbData == 1,
+         "Expected serial number size 1, got %ld\n",
+         entry->SerialNumber.cbData);
+        ok(*entry->SerialNumber.pbData == *serialNum,
+         "Expected serial number %d, got %d\n", *serialNum,
+         *entry->SerialNumber.pbData);
+        ok(info->Issuer.cbData == sizeof(encodedCommonName),
+         "Expected issuer of %d bytes, got %ld\n", sizeof(encodedCommonName),
+         info->Issuer.cbData);
+        ok(!memcmp(info->Issuer.pbData, encodedCommonName, info->Issuer.cbData),
+         "Unexpected issuer\n");
+        /* Oddly, the extensions don't seem to be decoded. Is this just an MS
+         * bug, or am I missing something?
+         */
+        ok(info->cExtension == 0, "Expected 0 extensions, got %ld\n",
+         info->cExtension);
+    }
+}
+
 static void test_registerOIDFunction(void)
 {
     static const WCHAR bogusDll[] = { 'b','o','g','u','s','.','d','l','l',0 };
@@ -1765,6 +2229,8 @@
         test_decodeFiletime(encodings[i]);
         test_encodeName(encodings[i]);
         test_decodeName(encodings[i]);
+        test_encodeAltName(encodings[i]);
+        test_decodeAltName(encodings[i]);
         test_encodeOctets(encodings[i]);
         test_decodeOctets(encodings[i]);
         test_encodeBits(encodings[i]);
@@ -1781,6 +2247,8 @@
         test_decodeCertToBeSigned(encodings[i]);
         test_encodeCert(encodings[i]);
         test_decodeCert(encodings[i]);
+        test_encodeCRLToBeSigned(encodings[i]);
+        test_decodeCRLToBeSigned(encodings[i]);
     }
     test_registerOIDFunction();
 }