Ошибка цифровой подписи RSA

Я пытаюсь реализовать схему цифровой подписи RSA Blind, используя класс BigInteger для генерации больших простых чисел. Саманта генерирует открытый ключ, закрытый ключ, выбирает сообщение, маскирует его, подписывает, а затем Виктор проверяет подпись.

Проблема: Пока я использую метод модульного возведения в степень modPow из класса BigInteger, все работает отлично (алгоритм проверки каждый раз возвращает true). Однако я создал собственный класс, в котором самостоятельно реализовал несколько алгебраических алгоритмов; когда я переключаю вызов modPow с помощью моего метода modExp, я продолжаю получать ложные результаты от алгоритма проверки (примерно в 50-60 % случаев), хотя я не должен . Если вместо использования больших случайных целых чисел я задаю маленькие, жестко запрограммированные числа для целей тестирования, я получаю правильный результат.

Вопрос: Как следствие, я почти уверен, что проблема в моем методе modExp, однако я не могу понять, сделал ли я что-то не так, даже после изменения алгоритм несколько раз. В чем проблема?

Мой код до сих пор:

RSA_test() -- метод, используемый для этапа предварительного вычисления и тестирования.

public static void RSA_test(){

    // The Signer (Samantha) picks p and q, 1024 bit primes
    Random rng = new SecureRandom();
    BigInteger p = BigInteger.probablePrime(1024, rng);
    BigInteger q = BigInteger.probablePrime(1024, rng);
  /*BigInteger p = BigInteger.valueOf(7);
    BigInteger q = BigInteger.valueOf(13);*/

    // The RSA modulus is computed
    BigInteger n = p.multiply(q);

    // phi(n) is computed
    BigInteger phiN = (p.subtract(BigInteger.ONE)
                      .multiply(q.subtract(BigInteger.ONE)));

    // Samantha chooses her message, m
    BigInteger m = new BigInteger("22");

    // Samantha computes her public exponent
    BigInteger v;
    while(true){
        v = new BigInteger(phiN.bitLength(), rng);
        if(v.compareTo(BigInteger.ONE) > 0 &&
           v.compareTo(phiN) < 0 &&
           ModularArithmetic.gcd(v, phiN).equals(BigInteger.ONE))
            break;
    }
    // v = BigInteger.valueOf(5);

    // Samantha generates the blinding factor and masks her message
    BigInteger r;
    while(true){
        r = new BigInteger(512, rng);
        if(ModularArithmetic.gcd(r, n).equals(BigInteger.ONE))
            break;
    }
    // r = BigInteger.valueOf(10);

    BigInteger mBlinded = m.multiply(ModularArithmetic.modExp(r, v, n));

    // Samantha signs her message
    BigInteger SBlinded = Cryptography.RSASignature(mBlinded, n, phiN, v);

    // Samantha removes the blinding factor, obtaining S
    BigInteger S = SBlinded.multiply(ModularArithmetic.modInv(r, n));

    // Victor verifies the signature
    boolean result = Cryptography.RSAVerification(S, m, n, v);

    String s = (result == true) ? "The signature has been verified" : "The signature has not been verified";
    System.out.println(s);
}

Поскольку методы подписи и проверки не имеют значения для вопроса, поскольку я уверен, что они верны, я их опускаю. Кроме того, вот мой метод modExp:

public static BigInteger modExp(BigInteger base, BigInteger exponent, BigInteger modulus){

    if(exponent.equals(BigInteger.ZERO))
        return (modulus.equals(BigInteger.ONE)) ? BigInteger.ZERO : BigInteger.ONE;
    if(base.equals(BigInteger.ONE))
        return (modulus.equals(BigInteger.ONE)) ? BigInteger.ZERO : BigInteger.ONE;
    if(exponent.equals(BigInteger.ONE))
        return base.mod(modulus);
    if(modulus.equals(BigInteger.ONE))
        return BigInteger.ZERO;
    // The case when base does not have a multiplicative inverse
    if((modulus.compareTo(BigInteger.ZERO) <= 0) || 
       ((exponent.compareTo(BigInteger.ZERO) < 0 && !(gcd(base,modulus).compareTo(BigInteger.ONE) == 0))))
        throw new ArithmeticException("BigInteger: modulus not positive");

    BigInteger result = BigInteger.ONE;
    while(exponent.compareTo(BigInteger.ZERO) > 0){
        if(exponent.testBit(0))
            result = (result.multiply(base).mod(modulus));
        exponent = exponent.shiftRight(1);
        base = (base.multiply(base)).mod(modulus);
    }

    return result.mod(modulus);
}

person Bogdan Kandra    schedule 01.11.2017    source источник
comment
У вас также есть собственная реализация gcd()?   -  person President James K. Polk    schedule 01.11.2017
comment
Да, знаю, почему ты спрашиваешь?   -  person Bogdan Kandra    schedule 01.11.2017
comment
Ну, вы не показали этот код, в случае, если это было частью проблемы, нам нужно было бы увидеть и это.   -  person President James K. Polk    schedule 02.11.2017
comment
Я протестировал его и локализовал проблему в алгоритме модульного возведения в степень. Как вы сказали в ответе, это была именно проблема. Ваше здоровье!   -  person Bogdan Kandra    schedule 02.11.2017


Ответы (1)


Вы неправильно обрабатываете отрицательные показатели степени, за исключением проверки того, что gcd(base, modulus) == 1. Следующий фрагмент показывает один правильный способ сделать это.

if (exponent.signum() < 0 && gcd(base,modulus).equals(BigInteger.ONE)) {
    return modExp(base.modInverse(modulus), exponent.negate(), modulus);
}

Обратите внимание, что signum() метод может быть более удобным для сравнения больших целых чисел с нулем.

person President James K. Polk    schedule 01.11.2017
comment
Да, очень красиво, я об этом не подумал. Спасибо. - person Bogdan Kandra; 01.11.2017