Указан неверный алгоритм только для Windows 2003 Server.

Я декодирую файл, используя следующий метод:

string outFileName = zfoFileName.Replace(".zfo", "_tmp.zfo");
FileStream inFile = null;
FileStream outFile = null;
inFile = File.Open(zfoFileName, FileMode.Open);
outFile = File.Create(outFileName);
LargeCMS.CMS cms = new LargeCMS.CMS();
cms.Decode(inFile, outFile);

Это отлично работает на моем компьютере с Win 7 dev, но на рабочем компьютере с сервером Windows 2003 происходит сбой со следующим исключением:

Исключение: System.Exception: ошибка CryptMsgUpdate #-2146893816 ---> System.ComponentModel.Win32Exception: указан недопустимый алгоритм --- Конец трассировки стека внутреннего исключения --- в LargeCMS.CMS.Decode(FileStream inFile, FileStream outFile)

Вот классы, ниже которых я призываю делать расшифровку, если нужно, могу выложить пример файла для расшифровки, просто странно, что работает на Win 7, а не на Win2k3 сервере:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Runtime.InteropServices;
using System.ComponentModel;

namespace LargeCMS
{
    class CMS
    {
        // File stream to use in callback function
        private FileStream m_callbackFile;

        // Streaming callback function for encoding
        private Boolean StreamOutputCallback(IntPtr pvArg, IntPtr pbData, int cbData, Boolean fFinal)
        {
            // Write all bytes to encoded file
            Byte[] bytes = new Byte[cbData];
            Marshal.Copy(pbData, bytes, 0, cbData);
            m_callbackFile.Write(bytes, 0, cbData);

            if (fFinal)
            {
                // This is the last piece. Close the file
                m_callbackFile.Flush();
                m_callbackFile.Close();
                m_callbackFile = null;
            }

            return true;
        }



        // Decode CMS with streaming to support large data
        public void Decode(FileStream inFile, FileStream outFile)
        {
            // Variables
            Win32.CMSG_STREAM_INFO StreamInfo;
            Win32.CERT_CONTEXT SignerCertContext;

            BinaryReader stream = null;
            GCHandle gchandle = new GCHandle();

            IntPtr hMsg = IntPtr.Zero;
            IntPtr pSignerCertInfo = IntPtr.Zero;
            IntPtr pSignerCertContext = IntPtr.Zero;
            IntPtr pbPtr = IntPtr.Zero;
            IntPtr hStore = IntPtr.Zero;
            Byte[] pbData;
            Boolean bResult = false;
            int dwFileSize;
            int dwRemaining;
            int dwSize;
            int cbSignerCertInfo;

            try
            {
                // Get data to decode
                dwFileSize = (int)inFile.Length;
                stream = new BinaryReader(inFile);
                pbData = stream.ReadBytes(dwFileSize);

                // Prepare stream for decoded info
                m_callbackFile = outFile;

                // Populate Stream Info struct
                StreamInfo = new Win32.CMSG_STREAM_INFO();
                StreamInfo.cbContent = dwFileSize;
                StreamInfo.pfnStreamOutput = new Win32.StreamOutputCallbackDelegate(StreamOutputCallback);

                // Open message to decode
                hMsg = Win32.CryptMsgOpenToDecode(
                    Win32.X509_ASN_ENCODING | Win32.PKCS_7_ASN_ENCODING,
                    0,
                    0,
                    IntPtr.Zero,
                    IntPtr.Zero,
                    ref StreamInfo
                );
                if (hMsg.Equals(IntPtr.Zero))
                {
                    throw new Exception("CryptMsgOpenToDecode error #" + Marshal.GetLastWin32Error().ToString(), new Win32Exception(Marshal.GetLastWin32Error()));
                }

                // Process the whole message
                gchandle = GCHandle.Alloc(pbData, GCHandleType.Pinned);
                pbPtr = gchandle.AddrOfPinnedObject();
                dwRemaining = dwFileSize;
                dwSize = (dwFileSize < 1024 * 1000 * 100) ? dwFileSize : 1024 * 1000 * 100;
                while (dwRemaining > 0)
                {
                    // Update message piece by piece     
                    bResult = Win32.CryptMsgUpdate(
                        hMsg,
                        pbPtr,
                        dwSize,
                        (dwRemaining <= dwSize) ? true : false
                    );
                    if (!bResult)
                    {
                        throw new Exception("CryptMsgUpdate error #" + Marshal.GetLastWin32Error().ToString(), new Win32Exception(Marshal.GetLastWin32Error()));
                    }

                    // Move to the next piece
                    pbPtr = new IntPtr(pbPtr.ToInt64() + dwSize);
                    dwRemaining -= dwSize;
                    if (dwRemaining < dwSize)
                    {
                        dwSize = dwRemaining;
                    }
                }

                // Get signer certificate info
                cbSignerCertInfo = 0;
                bResult = Win32.CryptMsgGetParam(
                    hMsg,
                    Win32.CMSG_SIGNER_CERT_INFO_PARAM,
                    0,
                    IntPtr.Zero,
                    ref cbSignerCertInfo
                );
                if (!bResult)
                {
                    throw new Exception("CryptMsgGetParam error #" + Marshal.GetLastWin32Error().ToString(), new Win32Exception(Marshal.GetLastWin32Error()));
                }

                pSignerCertInfo = Marshal.AllocHGlobal(cbSignerCertInfo);

                bResult = Win32.CryptMsgGetParam(
                    hMsg,
                    Win32.CMSG_SIGNER_CERT_INFO_PARAM,
                    0,
                    pSignerCertInfo,
                    ref cbSignerCertInfo
                );
                if (!bResult)
                {
                    throw new Exception("CryptMsgGetParam error #" + Marshal.GetLastWin32Error().ToString(), new Win32Exception(Marshal.GetLastWin32Error()));
                }

                // Open a cert store in memory with the certs from the message
                hStore = Win32.CertOpenStore(
                    Win32.CERT_STORE_PROV_MSG,
                    Win32.X509_ASN_ENCODING | Win32.PKCS_7_ASN_ENCODING,
                    IntPtr.Zero,
                    0,
                    hMsg
                );
                if (hStore.Equals(IntPtr.Zero))
                {
                    throw new Exception("CertOpenStore error #" + Marshal.GetLastWin32Error().ToString(), new Win32Exception(Marshal.GetLastWin32Error()));
                }

                // Find the signer's cert in the store
                pSignerCertContext = Win32.CertGetSubjectCertificateFromStore(
                    hStore,
                    Win32.X509_ASN_ENCODING | Win32.PKCS_7_ASN_ENCODING,
                    pSignerCertInfo
                );
                if (pSignerCertContext.Equals(IntPtr.Zero))
                {
                    throw new Exception("CertGetSubjectCertificateFromStore error #" + Marshal.GetLastWin32Error().ToString(), new Win32Exception(Marshal.GetLastWin32Error()));
                }

                // Set message for verifying
                SignerCertContext = (Win32.CERT_CONTEXT)Marshal.PtrToStructure(pSignerCertContext, typeof(Win32.CERT_CONTEXT));
                bResult = Win32.CryptMsgControl(
                    hMsg,
                    0,
                    Win32.CMSG_CTRL_VERIFY_SIGNATURE,
                    SignerCertContext.pCertInfo
                );
                if (!bResult)
                {
                    throw new Exception("CryptMsgControl error #" + Marshal.GetLastWin32Error().ToString(), new Win32Exception(Marshal.GetLastWin32Error()));
                }
            }
            finally
            {
                // Clean up
                if (gchandle.IsAllocated)
                {
                    gchandle.Free();
                }
                if (!pSignerCertContext.Equals(IntPtr.Zero))
                {
                    Win32.CertFreeCertificateContext(pSignerCertContext);
                }
                if (!pSignerCertInfo.Equals(IntPtr.Zero))
                {
                    Marshal.FreeHGlobal(pSignerCertInfo);
                }
                if (!hStore.Equals(IntPtr.Zero))
                {
                    Win32.CertCloseStore(hStore, Win32.CERT_CLOSE_STORE_FORCE_FLAG);
                }
                if (stream != null)
                {
                    stream.Close();
                }
                if (m_callbackFile != null)
                {
                    m_callbackFile.Close();
                }
                if (!hMsg.Equals(IntPtr.Zero))
                {
                    Win32.CryptMsgClose(hMsg);
                }
            }
        }
    }
}

и

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.ComponentModel;
using System.Security.Cryptography;

namespace LargeCMS
{
    class Win32
    {
        #region "CONSTS"

        public const int X509_ASN_ENCODING = 0x00000001;
        public const int PKCS_7_ASN_ENCODING = 0x00010000;
        public const int CMSG_SIGNED = 2;
        public const int CMSG_DETACHED_FLAG = 0x00000004;
        public const int AT_KEYEXCHANGE = 1;
        public const int AT_SIGNATURE = 2;
        public const String szOID_OIWSEC_sha1 = "1.3.14.3.2.26";
        public const int CMSG_CTRL_VERIFY_SIGNATURE = 1;
        public const int CMSG_CERT_PARAM = 12;
        public const int CMSG_SIGNER_CERT_INFO_PARAM = 7;
        public const int CERT_STORE_PROV_MSG = 1;
        public const int CERT_CLOSE_STORE_FORCE_FLAG = 1;

        #endregion

        #region "STRUCTS"

        [StructLayout(LayoutKind.Sequential)]
        public struct CRYPT_ALGORITHM_IDENTIFIER
        {
            public String pszObjId;
            BLOB Parameters;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct CERT_ID
        {
            public int dwIdChoice;
            public BLOB IssuerSerialNumberOrKeyIdOrHashId;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct CMSG_SIGNER_ENCODE_INFO
        {
            public int cbSize;
            public IntPtr pCertInfo;
            public IntPtr hCryptProvOrhNCryptKey;
            public int dwKeySpec;
            public CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
            public IntPtr pvHashAuxInfo;
            public int cAuthAttr;
            public IntPtr rgAuthAttr;
            public int cUnauthAttr;
            public IntPtr rgUnauthAttr;
            public CERT_ID SignerId;
            public CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm;
            public IntPtr pvHashEncryptionAuxInfo;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct CERT_CONTEXT
        {
            public int dwCertEncodingType;
            public IntPtr pbCertEncoded;
            public int cbCertEncoded;
            public IntPtr pCertInfo;
            public IntPtr hCertStore;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BLOB
        {
            public int cbData;
            public IntPtr pbData;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct CMSG_SIGNED_ENCODE_INFO
        {
            public int cbSize;
            public int cSigners;
            public IntPtr rgSigners;
            public int cCertEncoded;
            public IntPtr rgCertEncoded;
            public int cCrlEncoded;
            public IntPtr rgCrlEncoded;
            public int cAttrCertEncoded;
            public IntPtr rgAttrCertEncoded;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct CMSG_STREAM_INFO
        {
            public int cbContent;
            public StreamOutputCallbackDelegate pfnStreamOutput;
            public IntPtr pvArg;
        }

        #endregion

        #region "DELEGATES"

        public delegate Boolean StreamOutputCallbackDelegate(IntPtr pvArg, IntPtr pbData, int cbData, Boolean fFinal);

        #endregion

        #region "API"

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern Boolean CryptAcquireContext(
          ref IntPtr hProv,
          String pszContainer,
          String pszProvider,
          int dwProvType,
          int dwFlags
        );

        [DllImport("Crypt32.dll", SetLastError = true)]
        public static extern IntPtr CryptMsgOpenToEncode(
            int dwMsgEncodingType,
            int dwFlags,
            int dwMsgType,
            ref CMSG_SIGNED_ENCODE_INFO pvMsgEncodeInfo,
            String pszInnerContentObjID,
            ref CMSG_STREAM_INFO pStreamInfo
        );

        [DllImport("Crypt32.dll", SetLastError = true)]
        public static extern IntPtr CryptMsgOpenToDecode(
            int dwMsgEncodingType,
            int dwFlags,
            int dwMsgType,
            IntPtr hCryptProv,
            IntPtr pRecipientInfo,
            ref CMSG_STREAM_INFO pStreamInfo
        );

        [DllImport("Crypt32.dll", SetLastError = true)]
        public static extern Boolean CryptMsgClose(
            IntPtr hCryptMsg
        );

        [DllImport("Crypt32.dll", SetLastError = true)]
        public static extern Boolean CryptMsgUpdate(
            IntPtr hCryptMsg,
            Byte[] pbData,
            int cbData,
            Boolean fFinal
        );

        [DllImport("Crypt32.dll", SetLastError = true)]
        public static extern Boolean CryptMsgUpdate(
            IntPtr hCryptMsg,
            IntPtr pbData,
            int cbData,
            Boolean fFinal
        );

        [DllImport("Crypt32.dll", SetLastError = true)]
        public static extern Boolean CryptMsgGetParam(
            IntPtr hCryptMsg,
            int dwParamType,
            int dwIndex,
            IntPtr pvData,
            ref int pcbData
        );

        [DllImport("Crypt32.dll", SetLastError = true)]
        public static extern Boolean CryptMsgControl(
            IntPtr hCryptMsg,
            int dwFlags,
            int dwCtrlType,
            IntPtr pvCtrlPara
        );

        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern Boolean CryptReleaseContext(
            IntPtr hProv,
            int dwFlags
        );

        [DllImport("Crypt32.dll", SetLastError = true)]
        public static extern IntPtr CertCreateCertificateContext(
            int dwCertEncodingType,
            IntPtr pbCertEncoded,
            int cbCertEncoded
        );

        [DllImport("Crypt32.dll", SetLastError = true)]
        public static extern Boolean CertFreeCertificateContext(
            IntPtr pCertContext
        );

        [DllImport("Crypt32.dll", SetLastError = true)]
        public static extern IntPtr CertOpenStore(
            int lpszStoreProvider,
            int dwMsgAndCertEncodingType,
            IntPtr hCryptProv,
            int dwFlags,
            IntPtr pvPara
        );

        [DllImport("Crypt32.dll", SetLastError = true)]
        public static extern IntPtr CertGetSubjectCertificateFromStore(
            IntPtr hCertStore,
            int dwCertEncodingType,
            IntPtr pCertId
        );

        [DllImport("Crypt32.dll", SetLastError = true)]
        public static extern IntPtr CertCloseStore(
            IntPtr hCertStore,
            int dwFlags
        );

        #endregion
    }
}

person JL.    schedule 21.04.2010    source источник
comment
Это было бы легче понять, если бы код был сокращен, чтобы просто показать проблему (вы спрашиваете о декодировании: удалите кодирование, объявления не используются...).   -  person Richard    schedule 26.04.2010
comment
Подробная информация о том, что вы пытаетесь сделать, также поможет.   -  person Richard    schedule 26.04.2010
comment
Хороший вопрос, я удалю encode.   -  person JL.    schedule 26.04.2010
comment
Я включил больше комментариев в код из своего ответа, чтобы показать, где именно следует вставлять новые строки.   -  person Oleg    schedule 28.04.2010


Ответы (3)


Можете ли вы дать больше информации о вашей среде. Прежде всего: какой пакет обновления имеет Windows 2003 Server. Например, существует ошибка «Выбор сертификата Diffie-Hellman SChannel по умолчанию на веб-странице регистрации вызывает ошибку: 0x80090008 — NTE_BAD_ALGID», которая исправлена ​​в SP3 http://support.microsoft.com/kb/324953/en. Если это не ваша проблема, вы должны разместить сертификат и двоичный файл с тестовым сообщением где-нибудь в Интернете и опубликовать URL-адрес здесь. Тогда можно будет воспроизвести и проверить проблему.

Полагаю, для решения вашей проблемы (если на Windows Server 2003 установлен последний Service Pack) придется изменить некоторые свойства сертификата, которым подписано сообщение.

Кстати, я не думаю, что в вашем сертификате используются алгоритмы SHA-2 (SHA-256, SHA-384 и SHA-512). Если вы используете этот и у вас установлен последний пакет обновления, то может потребоваться явное использование «Microsoft Enhanced RSA и AES Cryptographic Provider» (или «Microsoft Enhanced RSA и AES Cryptographic Provider (Prototype)», как это называется в Windows XP SP3) или PROV_RSA_AES или MS_ENH_RSA_AES_PROV вместо поставщика PROV_RSA_FULL по умолчанию. (см., например, http://blogs.msdn.com/alejacma/archive/2009/01/23/sha-2-support-on-windows-xp.aspx)

Обновлено 1: после получения тестового файла проблема стала более ясной. Прежде всего хорошая новость. Ваша программа работает правильно! Она работает без проблем на моем тестовом сервере Windows 2003 с SP2. Таким образом, у нас административная проблема, а не разработка программного обеспечения. Я рекомендую вам протестировать программу на другом сервере Windows 2003. На этом сервере вы можете переустановить SP2, а затем перейти в Microsoft Updates и установить все обновления.

Кстати, у вас нет проблем с SHA256 или другими алгоритмами SHA-2. Вы используете в своем примере стандартный алгоритм 1.2.840.113549.1.1.5 sha1RSA.

Теперь о вашей программе. Я подробно прочитал ваш код и точно понимаю, что вы делаете. Вы получаете подписанное сообщение PKCS#7, содержащее текстовую плитку (файл XML) внутри. Насколько я понимаю, ваш пример взят из http://blogs.msdn.com/alejacma/archive/2010/04/09/how-to-call-cryptmsg-api-in-streaming-mode-c.aspx, в котором описывается проблема с расшифровкой файлов размером более 100 МБ (см. также http://blogs.msdn.com/alejacma/archive/2010/03/17/asn1-value-too-large-error-when-calling-signedcms-computesignature.aspx). Если у вас нет этого случая, я рекомендую вам использовать криптографические функции .NET из пространства имен System.Security.Cryptography.Pkcs. Если у вас есть большие данные, ваш текущий код в порядке. Единственное подозрительное место - это чтение входного файла. Я не читаю вызов stream.ReadBytes(). Я бы лучше использовал файлы с отображением памяти вместо загрузки огромного файла в память. Чтобы сделать это в собственном коде, вы можете использовать следующий код

DWORD MapFileInMemory (IN LPCWSTR pszFileName,
                       OUT PBYTE *ppbyFile, OUT PDWORD pdwFileSizeLow, OUT PDWORD pdwFileSizeHigh)
{
    HANDLE hFile = INVALID_HANDLE_VALUE, hFileMapping = NULL;
    DWORD  dwStatus = (DWORD)E_UNEXPECTED;

    __try {
        // Open the input file to be encrypted or decrypted
        hFile = CreateFileW (pszFileName, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING,
                             FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
        if (hFile == INVALID_HANDLE_VALUE) {
            dwStatus = GetLastError();
            __leave;
        }

        *pdwFileSizeLow = GetFileSize (hFile, pdwFileSizeHigh);
        if (*pdwFileSizeLow == INVALID_FILE_SIZE){
            dwStatus = GetLastError();
            __leave;
        }

        hFileMapping = CreateFileMapping (hFile, NULL, PAGE_READONLY, 0, 0, NULL);
        if (!hFileMapping){
            dwStatus = GetLastError();
            __leave;
        }

        *ppbyFile = (PBYTE) MapViewOfFile (hFileMapping, FILE_MAP_READ, 0, 0, 0);
        if (*ppbyFile == NULL) {
            dwStatus = GetLastError();
            __leave;
        }

        dwStatus = NO_ERROR;
    }
    __finally {
        if (hFileMapping)
            CloseHandle (hFileMapping);

        if (hFile != INVALID_HANDLE_VALUE)
            CloseHandle (hFile);
    }

    return dwStatus;
}

BOOL UnmapFileFromMemory (LPCVOID lpBaseAddress)
{
    return UnmapViewOfFile (lpBaseAddress);
}

Написание соответствующего .NET-кода не составит труда. Используя Memory Mapped Files, создайте только сопоставление виртуального адреса с файлом, данные будут считаны только в том случае, если вы получите доступ к соответствующей части данных.

Еще одно замечание. Часть вашего кода, где вы проверяете сообщение, не заполнена. Что вам нужно сделать, так это проверить сертификат, которым подписано сообщение. Если вы используете собственный CryptoAPI, вы можете сделать это в отношении CertGetCertificateChain(). Только тогда вы будете уверены, что сертификат и все его родители действительны. Вы также должны проверить в отношении CertVerifyCertificateChainPolicy(), что цепочка сертификатов разрешает использование сертификата для подписи сообщений.

Текущий код, кстати, работает без сообщений об ошибках, но эмитентом сертификата, которым подписано сообщение, является "CN=PostSignum Qualified CA, O="Ceská pošta, s.p. [IC 47114983]", C=CZ", а внутри вашего сообщения сертификат не существует. Вы можете использовать, например, certutil.exe -dump 31602.zfo, чтобы увидеть подробности:

Missing Issuer: CN=PostSignum Qualified CA, O="Ceská pošta, s.p. [IC 47114983]", C=CZ
  Issuer: CN=PostSignum Qualified CA, O="Ceská pošta, s.p. [IC 47114983]", C=CZ
  NotBefore: 03.12.2009 11:23
  NotAfter: 03.12.2010 10:33
  Subject: SERIALNUMBER=S7464, CN=Informacní systém datových schránek - zkušební prostredí, O="Ceská pošta, s.p. [IC 47114983]", C=CZ
  Serial: 04d3c5
  SubjectAltName: RFC822 [email protected], Other Name:Description=13 00
  59 c7 20 ba 70 b1 e6 93 ea c4 83 4b 3c 1e 35 dc b9 15 f5 ff
A certificate chain could not be built to a trusted root authority. 0x800b010a (-2146762486)

Вероятно, вы не хотите интерпретировать любое подписанное сообщение как действительное. Проверка сертификата обязательна. Более того, во многих сценариях было бы неплохо определить диапазон источников, из которых эмитент вы хотите разрешить подписанные сообщения в качестве входных данных. Думать об этом.

Обновлено 2: Вы правы в новом файле 331879.zfo, вы действительно используете sha256RSA (1.2.840.113549.1.1.11) для подписи. Попробуйте установить http://support.microsoft.com/kb/968730/en. которые мне нравятся
http://thehotfixshare.net/board/index.php?showtopic=12629&hl=968730. Это файл с цифровой подписью. Так что я должен быть в безопасности. Чтобы быть абсолютно уверенным, что вы можете получить это исправление от Microsoft. Я надеюсь, что это исправление решит вашу проблему.

Обновлено 3: я еще немного подумал о вашем примере кода. Мне кажется, что для получения наилучшей реализации нужно весь код декодирования сообщения реализовать как неуправляемый (нативный) код. Таким образом, вы не будете тратить дополнительное время на маршалинг между нативным и управляемым кодом при декодировании больших данных. Этот собственный код вы должны поместить в DLL и экспортировать функцию, которую вы можете использовать внутри вашего основного управляемого кода.

Еще одно замечание по поводу использования файла с отображением памяти. Использование файла с отображением памяти в основном оптимизировано для доступа к любому файлу в Windows как для чтения, так и для записи. Одна вещь, которую вы должны знать, это использование памяти. Если вы посмотрите на используемую память в диспетчере задач, вы увидите, что программа, использующая технику файлов с отображением памяти, может использовать очень большой объем памяти. Это совсем не проблема. Эта память не является физической памятью и не выгружаемой памятью из файла подкачки. Виртуальный адрес будет сопоставлен непосредственно с файлом, который вы сопоставили в памяти. Таким образом, подкачка данных будет выполняться относительно самих данных файла. Никаких дополнительных частей файла подкачки операционной системы не требуется. Этот ввод-вывод из файла значительно оптимизирован и реализован с учетом соответствующих функций встроенного процессора.

Конечное решение. Поскольку я не мог перестать думать об этой проблеме, мне пришлось ее решить. Вот решение, полностью основанное на том, что я уже писал ранее.

public const int PROV_RSA_AES = 24;
public const String MS_ENH_RSA_AES_PROV =
    "Microsoft Enhanced RSA and AES Cryptographic Provider";
public const String MS_ENH_RSA_AES_PROV_XP =
    "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)";
public const int CRYPT_VERIFYCONTEXT = unchecked((int)0xF0000000U);
[StructLayout (LayoutKind.Sequential)]
    public struct OSVERSIONINFOEX {
        public int dwOSVersionInfoSize;
        public int dwMajorVersion;
        public int dwMinorVersion;
        public int dwBuildNumber;
        public int dwPlatformId;
        [MarshalAs (UnmanagedType.ByValTStr, SizeConst = 128)]
        public string szCSDVersion;
        public short wServicePackMajor;
        public short wServicePackMinor;
        public short wSuiteMask;
        public byte wProductType;
        public byte wReserved;
    }
[DllImport ("kernel32.dll")]
public static extern bool GetVersionEx (ref OSVERSIONINFOEX osVersionInfo);
  • Измените public void Decode(FileStream inFile, FileStream outFile), чтобы явно использовать RSA и AES Cryptographic Provider в Windows Server 2003 или XP.
// insert next line before of try block like after this line
IntPtr hStore = IntPtr.Zero;
IntPtr hProv = IntPtr.Zero;

//...
// insert Windows versions test before CryptMsgOpenToDecode like after this line
StreamInfo.pfnStreamOutput = new Win32.StreamOutputCallbackDelegate(StreamOutputCallback);

Win32.OSVERSIONINFOEX osVersionInfo = new Win32.OSVERSIONINFOEX ();
osVersionInfo.dwOSVersionInfoSize = Marshal.SizeOf (typeof (Win32.OSVERSIONINFOEX));
if (Win32.GetVersionEx (ref osVersionInfo)) {
    Console.WriteLine ("dwMajorVersion={0}, dwMinorVersion={1}, wProductType={2}",
        osVersionInfo.dwMajorVersion, osVersionInfo.dwMinorVersion, osVersionInfo.wProductType);
    if (osVersionInfo.dwMajorVersion == 5 &&
        (osVersionInfo.dwMinorVersion == 2 || osVersionInfo.dwMinorVersion == 1)) {
        // Windows 2003 or XP
        string provider = Win32.MS_ENH_RSA_AES_PROV;
        if (osVersionInfo.dwMajorVersion == 5 && osVersionInfo.dwMinorVersion == 1)
            provider = Win32.MS_ENH_RSA_AES_PROV_XP;
        Win32.CryptAcquireContext (ref hProv, null, provider,
            Win32.PROV_RSA_AES, Win32.CRYPT_VERIFYCONTEXT);
    }
}

// Open message to decode
hMsg = Win32.CryptMsgOpenToDecode(
    Win32.X509_ASN_ENCODING | Win32.PKCS_7_ASN_ENCODING,
    0,
    0,
    hProv,
    IntPtr.Zero,
    ref StreamInfo
);
  • после декодирования закрыть дескриптор относительно функции CryptReleaseContext
    //...
    // insert CryptReleaseContext somewhere inside of finally block like after this line
    if (!hMsg.Equals(IntPtr.Zero))
    {
    Win32.CryptMsgClose(hMsg);
    }
    if (hProv != IntPtr.Zero)
    Win32.CryptReleaseContext (hProv, 0);

Теперь программа работает с данными, подписанными с помощью алгоритмов SHA-2 (например, 331879.zfo, подписанный с помощью 1.2.840.113549.1.1.11 sha256RSA)

Я рекомендую вам не забывать о файлах с отображением памяти. Если вы используете .NET 4.0, вы можете использовать новые классы платформы .NET (см. http://msdn.microsoft.com/en-us/library/dd997372%28v=VS.100%29.aspx).

person Oleg    schedule 26.04.2010
comment
У вас установлены постфиксы, такие как support.microsoft.com/kb/968730/en? ? Подробная информация о сертификате, используемом для подписи, была бы очень полезна (или о самом сертификате). - person Oleg; 26.04.2010
comment
В основном у меня есть формат файла (zfo), пример можно найти здесь: cid-d53719d869fb8243.skydrive.live.com/self.aspx/Public/, не знаю, как это подписано, извините, но я проверю исправления и вернусь. - person JL.; 26.04.2010
comment
KB 938397 — единственное существенное исправление, которое было установлено. - person JL.; 26.04.2010
comment
хорошо, файл, похоже, использует кодировку SHA256, если это поможет. - person JL.; 26.04.2010
comment
Привет, Олег, спасибо за помощь, я понял, что образец, который я использовал, был старым, вот новый формат, который действительно использует SHA2 — cid-d53719d869fb8243.skydrive.live.com/self.aspx/Public/ - person JL.; 27.04.2010
comment
Привет, Олег, вау, я сейчас провожу некоторые тесты, но пока все выглядит очень многообещающе. Я просто не могу понять, где в коде должен быть дескриптор закрытия, не могли бы вы обновить ответ, чтобы показать, где именно if (hProv != IntPtr.Zero) Win32.CryptReleaseContext (hProv, 0); надо идти, спасибо. - person JL.; 28.04.2010
comment
Большое спасибо, Олег, что с вашей стороны потребовалось много умения, чтобы найти и исправить эту проблему. Пока это работает как шарм. Файлы, которые я расшифровываю, не превышают 15 МБ. Вы по-прежнему рекомендуете использовать файлы с отображением памяти? И, кстати, материалы cryptoAPI .net вызывали ошибку в файлах размером около 7 МБ, отсюда и обходной путь largeCMS. - person JL.; 28.04.2010
comment
Я лично стараюсь использовать в своих программах максимально эффективную технику, если это возможно. Файлы с отображением памяти в мире Windows являются самым быстрым способом доступа к файлам. Логика вашей программы будет очень проста. Вместо операции ввода-вывода вы сопоставляете весь файл с указателем памяти и работаете с памятью. Ваша программа считывает себя с диска так же, как она читает из файла подкачки. Вы можете получить оптимизацию, если сделаете мэппинг блоков по 16К (или несколько по 16К) вместо мэппинга всего файла. Если у вас есть программа, написанная с функцией чтения файла, вы заменяете чтение отображением, и это работает! - person Oleg; 28.04.2010

Может ли быть так, что CSP, который вы используете, не установлен на вашей Win2003? Я помню, как читал, что в XP до SP3 были проблемы с SHA-2 или чем-то подобным, а XP и Win2003, как мне кажется, более или менее одинаковые базовые ОС.

Я думаю, вы можете увидеть, что установлено в следующем разделе реестра
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults

Может быть, вы можете сравнить между вашими машинами.

person Hans Olsson    schedule 21.04.2010
comment
Спасибо, это может помочь мне диагностировать проблему, и я уверен, что вы правы, теперь вопрос в том, как это исправить. - person JL.; 22.04.2010

MD5 и другие устаревшие/сломанные криптографические алгоритмы можно отключить с помощью групповой политики (необходимо для использования правительством США).

person Richard    schedule 26.04.2010