Как создать частный ключ RSA, используя модуль D, показатель степени в C #? - PullRequest
1 голос
/ 13 июня 2009

У меня есть 3 байтовых массива длиной 128, 128, 3 байта соответственно. Я не знаю, что это такое, но я ожидаю, что они будут Modulus, D, Exponent. Теперь, как я могу использовать эти массивы в C # для расшифровки массива байтов с использованием RSA? Когда я создаю RSAParameters и назначаю 3-байтовые массивы Modulus, D, Exponent и пытаюсь использовать эти RSAParameters в RSACryptoServiceProvider.ImportParameters, дешифрование завершается неудачно с указанием поврежденных ключей. Я предполагаю, что другие записи также должны быть заполнены DQ, DP, ... и т.д ...

Как мне это сделать в C #? У меня нет этих значений, есть ли простой способ расшифровать байтовый массив, используя только модули, D, экспоненту в C #, как в других языках?

Ответы [ 2 ]

1 голос
/ 08 июня 2017

Кажется, что реализации Windows желают выполнять RSA только через параметры CRT, оставляя D в качестве потенциально игнорируемого значения. По крайней мере, параметры CRT являются обязательными входными данными.

Во-первых, нам нужно превратить ваши массивы в значения BigInteger. Здесь я предполагаю, что у вас есть закодированные значения Big-Endian. Если они Little-Endian, не вызывайте Array.Reverse() и измените индекс копирования с 1 на 0.

private static BigInteger GetBigInteger(byte[] bytes)
{
    byte[] signPadded = new byte[bytes.Length + 1];
    Buffer.BlockCopy(bytes, 0, signPadded, 1, bytes.Length);
    Array.Reverse(signPadded);
    return new BigInteger(signPadded);
}

Добавление дополнительного байта предотвращает обработку чисел как отрицательных. (Можно было бы избежать выделения и копирования памяти, проверяя бит знака в последнем байте, если захотите).

Итак, теперь у вас есть три значения BigInteger, n, e, d. Не уверен, какой из n и d какой?

// Unless someone tried really hard to make this break it'll work.
if (n < d)
{
    BigInteger tmp = n;
    n = d;
    d = tmp;
}

Теперь, используя алгоритм из Специальная публикация NIST 800-56B Рекомендация для парных августовских схем построения ключей от августа 2009 г. с использованием целочисленной криптографии, Приложение C (как указано в https://stackoverflow.com/a/28299742/6535399), мы можем вычислить значения BigInteger. Однако здесь есть небольшая хитрость. Значения RSAParameters должны иметь правильное количество отступов, а RSACryptoServiceProvider не делает этого за вас.

private static RSAParameters RecoverRSAParameters(BigInteger n, BigInteger e, BigInteger d)
{
    using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
    {
        BigInteger k = d * e - 1;

        if (!k.IsEven)
        {
            throw new InvalidOperationException("d*e - 1 is odd");
        }

        BigInteger two = 2;
        BigInteger t = BigInteger.One;

        BigInteger r = k / two;

        while (r.IsEven)
        {
            t++;
            r /= two;
        }

        byte[] rndBuf = n.ToByteArray();

        if (rndBuf[rndBuf.Length - 1] == 0)
        {
            rndBuf = new byte[rndBuf.Length - 1];
        }

        BigInteger nMinusOne = n - BigInteger.One;

        bool cracked = false;
        BigInteger y = BigInteger.Zero;

        for (int i = 0; i < 100 && !cracked; i++)
        {
            BigInteger g;

            do
            {
                rng.GetBytes(rndBuf);
                g = GetBigInteger(rndBuf);
            }
            while (g >= n);

            y = BigInteger.ModPow(g, r, n);

            if (y.IsOne || y == nMinusOne)
            {
                i--;
                continue;
            }

            for (BigInteger j = BigInteger.One; j < t; j++)
            {
                BigInteger x = BigInteger.ModPow(y, two, n);

                if (x.IsOne)
                {
                    cracked = true;
                    break;
                }

                if (x == nMinusOne)
                {
                    break;
                }

                y = x;
            }
        }

        if (!cracked)
        {
            throw new InvalidOperationException("Prime factors not found");
        }

        BigInteger p = BigInteger.GreatestCommonDivisor(y - BigInteger.One, n);
        BigInteger q = n / p;
        BigInteger dp = d % (p - BigInteger.One);
        BigInteger dq = d % (q - BigInteger.One);
        BigInteger inverseQ = ModInverse(q, p);

        int modLen = rndBuf.Length;
        int halfModLen = (modLen + 1) / 2;

        return new RSAParameters
        {
            Modulus = GetBytes(n, modLen),
            Exponent = GetBytes(e, -1),
            D = GetBytes(d, modLen),
            P = GetBytes(p, halfModLen),
            Q = GetBytes(q, halfModLen),
            DP = GetBytes(dp, halfModLen),
            DQ = GetBytes(dq, halfModLen),
            InverseQ = GetBytes(inverseQ, halfModLen),
        };
    }
}

С помощью сложного метода BigInteger-to-fit-for-RSAParameters-byte []:

private static byte[] GetBytes(BigInteger value, int size)
{
    byte[] bytes = value.ToByteArray();

    if (size == -1)
    {
        size = bytes.Length;
    }

    if (bytes.Length > size + 1)
    {
        throw new InvalidOperationException($"Cannot squeeze value {value} to {size} bytes from {bytes.Length}.");
    }

    if (bytes.Length == size + 1 && bytes[bytes.Length - 1] != 0)
    {
        throw new InvalidOperationException($"Cannot squeeze value {value} to {size} bytes from {bytes.Length}.");
    }

    Array.Resize(ref bytes, size);
    Array.Reverse(bytes);
    return bytes;
}

А для вычисления InverseQ вам понадобится ModInverse:

private static BigInteger ModInverse(BigInteger e, BigInteger n)
{
    BigInteger r = n;
    BigInteger newR = e;
    BigInteger t = 0;
    BigInteger newT = 1;

    while (newR != 0)
    {
        BigInteger quotient = r / newR;
        BigInteger temp;

        temp = t;
        t = newT;
        newT = temp - quotient * newT;

        temp = r;
        r = newR;
        newR = temp - quotient * newR;
    }

    if (t < 0)
    {
        t = t + n;
    }

    return t;
}

На моем компьютере я восстанавливаю P и Q из (n, e, d) за ~ 50 мс для 1024-битного ключа. ~ 2-4 секунды для 4096-битного ключа.

Примечание для разработчиков, которым нравятся модульные тесты: на самом деле не существует определенного порядка для P и Q (как соглашение, согласно которому P всегда будет больше), поэтому ваши значения P и Q могут быть обратными от структуры RSAParameters, с которой вы начали , DP и DQ, таким образом, также поменяются местами.

0 голосов
/ 16 июля 2009

Вам не хватает, когда у вас есть только мод, D и показатель степени. (Ну, у вас может быть достаточно) P и Q ОЧЕНЬ сложно вычислить из мода. Я бы не знал, как это сделать, и почти наверняка было бы больше простых чисел, чем умноженных правильных, заканчивающихся тем же модом.

Вам нужно хотя бы P, Q и публичный показатель.

P, Q and D are the building blocks

DP = D mod (p - 1)
DQ = D mod (q - 1)
InverseQ = Q^-1 mod p
Modulus = P * Q

so now we have 

P Q and D.

and we can calulate DP, DQ, InverseQ and Modulus and Exponent (see below)

long gcd(long a, long b)
{
    long temp;
    while (b != 0)
    {
        temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

Exponent = gcd(1, (P - 1)*(Q - 1));
...