Подпись RSA с хешем MD5 + SHA1 в C # - PullRequest
0 голосов
/ 11 октября 2019

Мне нужно RSA sign некоторых данных, используя комбинацию хешей MD5 и SHA1. Я легко могу сделать это только для одного или другого, но наличие комбинации усложняет задачу. Это для сообщения certificate verify в DTLS 1.0, поэтому я не могу просто выбрать один, к сожалению.

Я считаю, что шаги должны состоять в том, чтобы хешировать в MD5, а затем хэш в SHA1 и объединить. Тогда мне нужно зашифровать с закрытым ключом. Вот где я застрял.

У меня есть RSACryptoServiceProvider, который имеет правильный закрытый ключ (он не экспортируется). Я знаю, что не могу сделать

rsa.SignHash(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);

, потому что это не просто хэш SHA1, поэтому эта функция считает хеш недействительным, поскольку он составляет 36 байтов, а не 20 байтов, как ожидалось.

Я также знаю, что яне могу сделать

rsa.SignData(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);

, потому что это снова будет хэшироваться.

Я считаю, что я ближе с

rsa.Encrypt(hash, RSAEncryptionPadding.Pkcs1);

, однако это использует открытый ключ вместо частногоключ, который мне нужен.

Есть предложения о том, как я могу либо зашифровать с помощью закрытого ключа, либо выполнить подпись с помощью MD5 + SHA1?

1 Ответ

1 голос
/ 14 октября 2019

Вы не можете сделать это в управляемом слое, но вы можете заставить собственный слой сделать это за вас.

Учитывая, что у вас уже есть постоянный ключ в формате RSACryptoServiceProvider (названный rsaCsp)и ваши данные для подписи (с именем toBeSigned) вы можете

  • повторно открыть ключ CAPI в CNG (почти всегда работает)
  • Попросить CNG сделать подпись для вас
    • Не указывая имя алгоритма хеширования, вы предполагаете, что вы сделали все, что требуется для формирования данных. В вашем случае просто объедините данные и не включайте их в DigestInfo.

Звучит сложно, но на самом деле это не так.

byte[] md5;
byte[] sha1;

using (HashAlgorithm hash = MD5.Create())
{
    md5 = hash.ComputeHash(toBeSigned);
}

using (HashAlgorithm hash = SHA1.Create())
{
    sha1 = hash.ComputeHash(toBeSigned);
}

byte[] all = md5.Concat(sha1).ToArray();
CspKeyContainerInfo cspInfo = rsaCsp.CspKeyContainerInfo;
CngProvider provider = new CngProvider(cspInfo.ProviderName);
CngKeyOpenOptions options = CngKeyOpenOptions.None;

if (cspInfo.MachineKeyStore)
{
    options = CngKeyOpenOptions.MachineKey;
}

using (CngKey cngKey = CngKey.Open(cspInfo.KeyContainerName, provider, options))
{
    return NCryptInterop.SignHashRaw(cngKey, all, rsaCsp.KeySize);
}

Это требует от вас небольшого усилия, если Interop работает:

private static class NCryptInterop
{
    private struct BCRYPT_PKCS1_PADDING_INFO
    {
        internal IntPtr pszAlgId;
    }

    [Flags]
    private enum NCryptSignFlags
    {
        BCRYPT_PAD_PKCS1 = 2,
    }

    [DllImport("ncrypt.dll")]
    private static extern int NCryptSignHash(
        SafeNCryptKeyHandle hKey,
        ref BCRYPT_PKCS1_PADDING_INFO padding,
        ref byte pbHashValue,
        int cbHashValue,
        ref byte pbSignature,
        int cbSignature,
        out int cbResult,
        NCryptSignFlags dwFlags);

    internal static byte[] SignHashRaw(CngKey key, byte[] hash, int keySize)
    {
        int keySizeBytes = keySize / 8;
        byte[] signature = new byte[keySizeBytes];

        // The Handle property returns a new object each time.
        using (SafeNCryptKeyHandle keyHandle = key.Handle)
        {
            // Leave pszAlgId NULL to "raw sign"
            BCRYPT_PKCS1_PADDING_INFO paddingInfo = new BCRYPT_PKCS1_PADDING_INFO();

            int result = NCryptSignHash(
                keyHandle,
                ref paddingInfo,
                ref hash[0],
                hash.Length,
                ref signature[0],
                signature.Length,
                out int cbResult,
                NCryptSignFlags.BCRYPT_PAD_PKCS1);

            if (result != 0)
            {
                throw new CryptographicException(result);
            }

            if (cbResult != signature.Length)
            {
                throw new InvalidOperationException();
            }

            return signature;
        }
    }
}

Теоретически можно было бы сделать это и с CAPI (если ваш закрытый ключ находится в HSM, в котором нет драйвера CNG)) ... но тогда вы должны играть с гораздо большим количеством указателей. Тип CngKey действительно экономит вам много работы здесь.

...