CryptoPro / corefx

This repo contains the .NET Core foundational libraries, called CoreFX. It includes classes for collections, file systems, console, XML, async and many others. We welcome contributions.
https://github.com/dotnet/core
MIT License
27 stars 7 forks source link

Проблема с определением типа KeyWrap для 12 госта (unix) #53

Open Fasjeit opened 2 years ago

Fasjeit commented 2 years ago

Сейчас для совместимости с некоторыми сторонними реализациями (ФСС) необходимо осуществлять импорт ключа 12 госта с использование старого CryptoProKeyWrap. Так как определить тип KeyWrap по ключу внутри Xml невозможно, то делаем так

private SymmetricAlgorithm CryptoProUnwrap(byte[] wrapped)
{
    try
    {
        return this.CryptoProUnwrap(wrapped, GostConstants.CALG_PRO12_EXPORT);
    }
    catch (CryptographicException ex)
    {
        if (Marshal.GetHRForException(ex) == -2146893819)
        {
            // bad data - пробуем импорт на старом алгоритме
            return this.CryptoProUnwrap(wrapped, GostConstants.CALG_PRO_EXPORT);
        }
        else
        {
            throw;
        }
    }
}

Проблема возникает при использовании метода ImportKeyBlob

 internal static int ImportKeyBlob(byte[] keyBlob,
            SafeProvHandle hProv, CspProviderFlags flags,
            SafeKeyHandle hImportKey, out SafeKeyHandle hKey)
{
    int keyFlags = MapCspKeyFlags((int)flags);
    bool ret = CapiHelper.CryptImportKey(hProv, keyBlob,
        keyBlob.Length, hImportKey, keyFlags, out hKey);
    if (!ret)
        throw new CryptographicException(Marshal.GetLastWin32Error());
    int algid_class = BitConverter.ToInt32(keyBlob, 4) & (7 << 13);
    if (algid_class == (5 << 13))
        return (int)KeyNumber.Exchange;
    return (int)KeyNumber.Signature;
}

Так как для Unix Marshal.GetLastWin32Error() вернёт некорректный код ошибки. При исправлении на

internal static int ImportKeyBlob(byte[] keyBlob,
            SafeProvHandle hProv, CspProviderFlags flags,
            SafeKeyHandle hImportKey, out SafeKeyHandle hKey)
{
    int keyFlags = MapCspKeyFlags((int)flags);
    bool ret = CapiHelper.CryptImportKey(hProv, keyBlob,
        keyBlob.Length, hImportKey, keyFlags, out hKey);
    if (!ret)
    {
        var hr = Interop.CPError.GetHRForLastWin32Error();
        throw hr.ToCryptographicException();
    }
    int algid_class = BitConverter.ToInt32(keyBlob, 4) & (7 << 13);
    if (algid_class == (5 << 13))
        return (int)KeyNumber.Exchange;
    return (int)KeyNumber.Signature;
}

работает корректно.

Проблема технически может возникать и в других сценариях, ибо код CapiHelper полностью перенесён из Шарпея, и обработка ошибок полностью виндовая, на unix возможны и другие некорректные коды. С виду мы больше нигде по ним не ветвимся, поэтому править массово пока не будем (из за опасений, что получение кода ошибки в других методах теоретически может приходить к перетиранию кода, и тогда будем получать ошибку и на windows).

Хорошо добывать бы тест на это (возможно в рамках LibCore с переносом сюда).