Почему я получаю это исключение, пытаясь расшифровать RSA с помощью Java, но зашифрованный с помощью PHP? - PullRequest
1 голос
/ 20 октября 2019

Я использую PHPSecLib для шифрования моего текста с помощью RSA:

      $rsa = new Crypt_RSA(); 
      $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);

      extract($rsa->createKey());
      $rsa->loadKey($privatekey);

      $ciphertext = $rsa->encrypt($plaintext);

      $rsa->loadKey($publickey);
      return base64_encode($ciphertext) . ":" . base64_encode($publickey);

Я получаю что-то вроде encryptedBased64: publicKeyBased64.

Кажется, что работает, и если я пытаюсь расшифровать с помощьютот же метод с PHP, он работает также. Но попытка расшифровать с помощью Java дает мне java.security.InvalidKeyException: неверный формат ключа. Это код:

    public static String decrypt(byte[] msg, byte[] key)
            throws Exception{

        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(key);
        PublicKey keys = keyFactory.generatePublic(publicKeySpec);

        Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding","BC");
        cipher.init(Cipher.DECRYPT_MODE, keys);
        return new String(cipher.doFinal(msg));
    }

    public static void main(String[] args) throws Exception{
      String res = "encryptedBased64:publicKeyBased64";
      decrypt(Base64.getDecoder().decode(res.split(":")[0]),Base64.getDecoder().decode(res.split(":")[1]));
    }

Не могу понять, почему.

1 Ответ

1 голос
/ 21 октября 2019
  • RSA используется в двух контекстах en- / расшифровывать и подписать / подтвердить . Для расшифровки / шифрования отправитель шифрует с помощью открытого ключа получателя, а получатель расшифровывает с помощью своего личного ключа. Для подписи / проверки отправитель подписывает свой личный ключ, а получатель проверяет с помощью открытого ключа отправителя. Это подробно объясняется здесь и здесь .

    На этом этапе становится ясно, что оба контекста были перепутаны в опубликованном коде: для расшифровки / расшифровки,отправитель шифрует сообщение своим личным ключом, а получатель расшифровывает его открытым ключом отправителя. Это не верно. Если это будет изменено в обоих кодах, как описано выше для расшифровки / расшифровки, расшифровка в Java также будет работать!

  • Чтобы понять, почему расшифровка работает в случае PHP, а не вВ случае Java, отступы должны быть рассмотрены. Из соображений безопасности на практике RSA всегда должен использоваться в сочетании с заполнением, например, PKCS1-v1_5-padding или OAEP. Однако заполнение отличается в зависимости от контекста. Для расшифровки / расшифровки используется RSAES-PKCS1-v1_5-padding и для подписи / проверки RSASSA-PKCS1-v1_5-padding. То же самое относится к OAEP (RSAES-OAEP и RSASSA-PSS, соответственно). Это подробно объясняется в RFC8017 и здесь . Важно, чтобы заполнение для расшифровки было идентично заполнению для шифрования. В противном случае расшифровка не удастся. То же относится и к подписи / проверке.

    В дальнейшем предполагается, что заполнение PKCS1-v1_5 используется так же, как и в опубликованном коде.

    В PHP / phpseclib, encrypt / decrypt -метод использует RSAES-PKCS1-v1_5-padding, независимо от того, используется ли открытый или закрытый ключ для шифрования (или дешифрования). Это означает, что encrypt / decrypt -метод всегда соответствует контексту en- / decrypting. Для подписи / проверки существуют соответствующие методы sign / verify. Это означает, что в опубликованном коде RSAES-PKCS1-v1_5-padding используется на стороне PHP для шифрования и дешифрования, поэтому дешифрование работает на стороне PHP.

    В Java класс Cipher определяет контекст или заполнение на основе комбинации режима и типа клавиши. Комбинации режим шифрования / открытый ключ - и режим дешифрования / закрытый ключ - определяют контекст en / / decrypting с RSAES-PKCS1-v1_5-padding, а другие комбинации определяют знак- / проверочный контекст с RSASSA-PKCS1-v1_5-padding. Это означает, что в размещенном коде RSASSA-PKCS1-v1_5-padding используется на стороне Java для расшифровки. Следовательно, расшифровка сообщения, зашифрованного в PHP, не выполняется в Java из-за различий в заполнении.

    Примечание. Обычно в Java класс Signature используется для подписи / проверки (и класс Cipher для шифрования / расшифровки).

  • Поскольку опубликованный код не раскрывает, что должно быть зашифровано, следует также отметить, что только RSA может быть зашифровано только относительно короткие сообщения и что для более длинных сообщений используется симметричный алгоритм, такой как AES. или оба процесса в комбинации ( гипридная криптосистема ).

...