Не удается восстановить закрытый ключ RSA по модулю и экспоненте - PullRequest
1 голос
/ 21 июня 2020

Я пытаюсь восстановить пару ключей RSA из экспонентов modulus и private / publi c. Преобразование работает правильно для ключа publi c, но не в закрытый ключ при сравнении закодированных закрытых ключей.

При использовании этой перестроенной пары ключей private / publi c для шифрования он работает (!) В Java, но при использовании пары ключей восстановления в PHP часть дешифрования не работает (шифрование работает), поэтому мне кажется, что закрытый ключ восстановления отличается от "исходного" закрытого ключа.

Просто для информации: с использованием «исходной» пары ключей все работает нормально в PHP.

Итак, мой вопрос: как мне получить «исходный» закрытый ключ из модуля (BigInteger) и частного показателя?

Изменить: см. Мое последнее изменение в конце

В моем примере кода показано равенство ключа publi c и ключа восстановления и что закрытые ключи различны:

Rebuilding of a RSA PrivateKey from modulus & exponent
privateKey equals rebuild: false
publicKey equals rebuild: true

код:

import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Arrays;

public class RebuildRSAPrivateKey {
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException {
        System.out.println("Rebuilding of a RSA PrivateKey from modulus & exponent");
        // rsa key generation
        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
        //kpGen.initialize(2048, new SecureRandom());
        kpGen.initialize(2048, new SecureRandom());
        KeyPair keyPair = kpGen.generateKeyPair();
        // private key
        PrivateKey privateKey = keyPair.getPrivate();
        // get modulus & exponent
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
        BigInteger modulus = rsaPrivateKey.getModulus();
        BigInteger privateExponent = rsaPrivateKey.getPrivateExponent();
        // rebuild the private key
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(modulus, privateExponent);
        PrivateKey privateKeyRebuild = keyFactory.generatePrivate(rsaPrivateKeySpec);
        System.out.println("privateKey equals rebuild: " + Arrays.equals(privateKey.getEncoded(), privateKeyRebuild.getEncoded()));
        // public key
        PublicKey publicKey = keyPair.getPublic();
        // get modulus & exponent
        RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
        BigInteger modulusPub = rsaPublicKey.getModulus();
        BigInteger publicExponent = rsaPublicKey.getPublicExponent();
        // rebuild the public key
        KeyFactory keyFactoryPub = KeyFactory.getInstance("RSA");
        RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(modulusPub, publicExponent);
        PublicKey publicKeyRebuild = keyFactory.generatePublic(rsaPublicKeySpec);
        System.out.println("publicKey equals rebuild: " + Arrays.equals(publicKey.getEncoded(), publicKeyRebuild.getEncoded()));
    }
}

Изменить: Следующие программы покажут, что пара ключей RSA private / publi c, полученная из закодированных ключей, может быть восстановлена ​​и в шифрование и дешифрование работает в Java и PHP. Ключи: небезопасные 512-битные ключи RSA и декодированные Base64.

Те же ключи затем получаются из модулей и частных / публикующихся c экспонент и работает шифрование / дешифрование. в Java, но не в PHP.

Вот почему я хотел бы получить "оригинальные" ключи RSA из модуля и экспоненты, спасибо за вашу любезную помощь.

Результат программы Java:

Rebuilding of a RSA PrivateKey from modulus & exponent v4
privateKey Original Base64: MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEA2wFgcni89ijJ/uijQkzCGF4JiUB1+mEJ48u4Lk0vxB7ym3/FCvOEnN2H7FLUzsGvXRhFriLBiSJlg2tOhV5eiwIDAQABAkEAkDpf4gNRrms+W/mpSshyKsoDTbh9+d5ePP601QlQI79lrsjdy2GLgk4RV1XmwYinM9Sk8G+ssyXTYHdby6A2wQIhAPcRtl6tub6PFiIE1jcuIkib/HzAdRYHZx3ZdzRTYDetAiEA4uv43xpGl5N8yG27Kv0DkRoOlr4Ch6oM24hLVw7ClhcCIFgdRAo+MQlqJH2bdf6WAHoez4x6YwepOjhmD2Jk/eK9AiEAtHgI6J5EEB56+gfS+CBa6tZ3Tcl1x6ElMp8Vk/ooJScCIQDUa3LUkcc58yjJYq8ZNQC/86+HIzd5MldTwg5buR1lpw==
privateKey Rebuild  Base64: MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEA2wFgcni89ijJ/uijQkzCGF4JiUB1+mEJ48u4Lk0vxB7ym3/FCvOEnN2H7FLUzsGvXRhFriLBiSJlg2tOhV5eiwIDAQABAkEAkDpf4gNRrms+W/mpSshyKsoDTbh9+d5ePP601QlQI79lrsjdy2GLgk4RV1XmwYinM9Sk8G+ssyXTYHdby6A2wQIhAPcRtl6tub6PFiIE1jcuIkib/HzAdRYHZx3ZdzRTYDetAiEA4uv43xpGl5N8yG27Kv0DkRoOlr4Ch6oM24hLVw7ClhcCIFgdRAo+MQlqJH2bdf6WAHoez4x6YwepOjhmD2Jk/eK9AiEAtHgI6J5EEB56+gfS+CBa6tZ3Tcl1x6ElMp8Vk/ooJScCIQDUa3LUkcc58yjJYq8ZNQC/86+HIzd5MldTwg5buR1lpw==
publicKey           Base64: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANsBYHJ4vPYoyf7oo0JMwhheCYlAdfphCePLuC5NL8Qe8pt/xQrzhJzdh+xS1M7Br10YRa4iwYkiZYNrToVeXosCAwEAAQ==

generate private & public key via modulus and private/public exponent
privateKey Modulus  Base64: MIGzAgEAMA0GCSqGSIb3DQEBAQUABIGeMIGbAgEAAkEA2wFgcni89ijJ/uijQkzCGF4JiUB1+mEJ48u4Lk0vxB7ym3/FCvOEnN2H7FLUzsGvXRhFriLBiSJlg2tOhV5eiwIBAAJBAJA6X+IDUa5rPlv5qUrIcirKA024ffneXjz+tNUJUCO/Za7I3cthi4JOEVdV5sGIpzPUpPBvrLMl02B3W8ugNsECAQACAQACAQACAQACAQA=
publicKey  Modulus  Base64: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANsBYHJ4vPYoyf7oo0JMwhheCYlAdfphCePLuC5NL8Qe8pt/xQrzhJzdh+xS1M7Br10YRa4iwYkiZYNrToVeXosCAwEAAQ==

en-/decryption with original keys
ciphertext Original   : fvFPRZ5B2GMgv9aXQjyQsxnRHK2wotfXlLV+zGea1E3nsZC6RMn+LQMOe9yvZ8IcaG2F/8wWv2NkNmBX4wuxaw==
decryptedtext Original: this is the message to encrypt

en-/decryption with keys from modulus & exponent
ciphertext Modulus    : o0tB4xQIwQRFDSsWj1WgWHexXnJOp9jeBymFPJvy+xZBvfJay2yR0XZEy+0VwaedxdTf9CoyKVvgCbn2HCohSQ==
decryptedtext Modulus : this is the message to encrypt

Результат программы PHP:

php version: 7.4.6 openssl version: OpenSSL 1.1.1g 21 Apr 2020
plaintext: this is the message to encrypt
rsa encryption with original keys
priBase64:MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEA2wFgcni89ijJ/uijQkzCGF4JiUB1+mEJ48u4Lk0vxB7ym3/FCvOEnN2H7FLUzsGvXRhFriLBiSJlg2tOhV5eiwIDAQABAkEAkDpf4gNRrms+W/mpSshyKsoDTbh9+d5ePP601QlQI79lrsjdy2GLgk4RV1XmwYinM9Sk8G+ssyXTYHdby6A2wQIhAPcRtl6tub6PFiIE1jcuIkib/HzAdRYHZx3ZdzRTYDetAiEA4uv43xpGl5N8yG27Kv0DkRoOlr4Ch6oM24hLVw7ClhcCIFgdRAo+MQlqJH2bdf6WAHoez4x6YwepOjhmD2Jk/eK9AiEAtHgI6J5EEB56+gfS+CBa6tZ3Tcl1x6ElMp8Vk/ooJScCIQDUa3LUkcc58yjJYq8ZNQC/86+HIzd5MldTwg5buR1lpw==
pubBase64:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANsBYHJ4vPYoyf7oo0JMwhheCYlAdfphCePLuC5NL8Qe8pt/xQrzhJzdh+xS1M7Br10YRa4iwYkiZYNrToVeXosCAwEAAQ==
ciphertext Base64:WmvVwqf2EHQc0yb6L4pVJ0/23pNW4QsBun3SNvYE8p/sEk+1GQSYxYpbY/mLbSGF2Lb1P5g5er+z7dWxHmodNA==
decryptedtext: this is the message to encrypt

rsa encryption with keys created via modulus & exponents
priBase64:MIGzAgEAMA0GCSqGSIb3DQEBAQUABIGeMIGbAgEAAkEA2wFgcni89ijJ/uijQkzCGF4JiUB1+mEJ48u4Lk0vxB7ym3/FCvOEnN2H7FLUzsGvXRhFriLBiSJlg2tOhV5eiwIBAAJBAJA6X+IDUa5rPlv5qUrIcirKA024ffneXjz+tNUJUCO/Za7I3cthi4JOEVdV5sGIpzPUpPBvrLMl02B3W8ugNsECAQACAQACAQACAQACAQA=
pubBase64:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANsBYHJ4vPYoyf7oo0JMwhheCYlAdfphCePLuC5NL8Qe8pt/xQrzhJzdh+xS1M7Br10YRa4iwYkiZYNrToVeXosCAwEAAQ==
ciphertext Base64:kqn8aZpvfpPzr3u2NBX/XmnlFweEvOm+Qu4l2wiUSQCjA0hutQ10mbLaO55oCox7GixvMgb3VtoDBJ8hfW1zbQ==
Cannot Decrypt error:0407109F:rsa routines:RSA_padding_check_PKCS1_type_2:pkcs decoding error
decryptedtext:

decrypt error: error:0909006C:PEM routines:get_name:no start line

Источник Java:

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.*;
import java.util.Base64;

public class RebuildRSAPrivateKey4 {
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchPaddingException, IOException {
        System.out.println("Rebuilding of a RSA PrivateKey from modulus & exponent v4");
        // rsa key generation
        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
        //kpGen.initialize(2048, new SecureRandom());
        kpGen.initialize(512, new SecureRandom()); // don't use 512 bit keys as they are insecure !!
        KeyPair keyPair = kpGen.generateKeyPair();

        // privateKey   Base64: MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEA2wFgcni89ijJ/uijQkzCGF4JiUB1+mEJ48u4Lk0vxB7ym3/FCvOEnN2H7FLUzsGvXRhFriLBiSJlg2tOhV5eiwIDAQABAkEAkDpf4gNRrms+W/mpSshyKsoDTbh9+d5ePP601QlQI79lrsjdy2GLgk4RV1XmwYinM9Sk8G+ssyXTYHdby6A2wQIhAPcRtl6tub6PFiIE1jcuIkib/HzAdRYHZx3ZdzRTYDetAiEA4uv43xpGl5N8yG27Kv0DkRoOlr4Ch6oM24hLVw7ClhcCIFgdRAo+MQlqJH2bdf6WAHoez4x6YwepOjhmD2Jk/eK9AiEAtHgI6J5EEB56+gfS+CBa6tZ3Tcl1x6ElMp8Vk/ooJScCIQDUa3LUkcc58yjJYq8ZNQC/86+HIzd5MldTwg5buR1lpw==
        // publicKey    Base64: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANsBYHJ4vPYoyf7oo0JMwhheCYlAdfphCePLuC5NL8Qe8pt/xQrzhJzdh+xS1M7Br10YRa4iwYkiZYNrToVeXosCAwEAAQ==
        String privateKeyBase64 = "MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEA2wFgcni89ijJ/uijQkzCGF4JiUB1+mEJ48u4Lk0vxB7ym3/FCvOEnN2H7FLUzsGvXRhFriLBiSJlg2tOhV5eiwIDAQABAkEAkDpf4gNRrms+W/mpSshyKsoDTbh9+d5ePP601QlQI79lrsjdy2GLgk4RV1XmwYinM9Sk8G+ssyXTYHdby6A2wQIhAPcRtl6tub6PFiIE1jcuIkib/HzAdRYHZx3ZdzRTYDetAiEA4uv43xpGl5N8yG27Kv0DkRoOlr4Ch6oM24hLVw7ClhcCIFgdRAo+MQlqJH2bdf6WAHoez4x6YwepOjhmD2Jk/eK9AiEAtHgI6J5EEB56+gfS+CBa6tZ3Tcl1x6ElMp8Vk/ooJScCIQDUa3LUkcc58yjJYq8ZNQC/86+HIzd5MldTwg5buR1lpw==";
        String publicKeyBase64 = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANsBYHJ4vPYoyf7oo0JMwhheCYlAdfphCePLuC5NL8Qe8pt/xQrzhJzdh+xS1M7Br10YRa4iwYkiZYNrToVeXosCAwEAAQ==";
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyBase64));
        PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyBase64));
        PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
        System.out.println("privateKey Original Base64: " + privateKeyBase64);
        System.out.println("privateKey Rebuild  Base64: " + Base64.getEncoder().encodeToString(privateKey.getEncoded()));
        System.out.println("publicKey           Base64: " + publicKeyBase64);
        // get modulus & private exponent via RSAPrivateKey
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
        BigInteger modulus = rsaPrivateKey.getModulus();
        BigInteger privateExponent = rsaPrivateKey.getPrivateExponent();
        // rebuild the private key
        RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(modulus, privateExponent);
        PrivateKey privateKeyModulusExponent = keyFactory.generatePrivate(rsaPrivateKeySpec);
        // public key
        RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
        BigInteger modulusPub = rsaPublicKey.getModulus();
        BigInteger publicExponent = rsaPublicKey.getPublicExponent();
        // rebuild the public key
        RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(modulusPub, publicExponent);
        PublicKey publicKeyModulusExponent = keyFactory.generatePublic(rsaPublicKeySpec);
        System.out.println("\ngenerate private & public key via modulus and private/public exponent");
        System.out.println("privateKey Modulus  Base64: " + Base64.getEncoder().encodeToString(privateKeyModulusExponent.getEncoded()));
        System.out.println("publicKey  Modulus  Base64: " + Base64.getEncoder().encodeToString(publicKeyModulusExponent.getEncoded()));
        System.out.println("\nen-/decryption with original keys");
        String plaintext = "this is the message to encrypt";
        String ciphertextOriginal = encrypt(publicKey, plaintext);
        String decryptedtextOriginal = decrypt(privateKey, ciphertextOriginal);
        System.out.println("ciphertext Original   : " + ciphertextOriginal);
        System.out.println("decryptedtext Original: " + decryptedtextOriginal);
        System.out.println("\nen-/decryption with keys from modulus & exponent");
        String ciphertextModulus = encrypt(publicKeyModulusExponent, plaintext);
        String decryptedtextModulus = decrypt(privateKeyModulusExponent, ciphertextOriginal);
        System.out.println("ciphertext Modulus    : " + ciphertextModulus);
        System.out.println("decryptedtext Modulus : " + decryptedtextModulus);
    }

    private static String encrypt(PublicKey publicKey, String plaintext) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException, BadPaddingException, IllegalBlockSizeException {
        String ciphertext = "";
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] ciphertextByte = cipher.doFinal(plaintext.getBytes("UTF8"));
        ciphertext = Base64.getEncoder().encodeToString(ciphertextByte).replaceAll("\\r|\\n", "");
        return ciphertext;
    }

    private static String decrypt(PrivateKey privateKey, String ciphertext) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] ciphertextByte = Base64.getDecoder().decode(ciphertext);
        byte[] decryptedtextByte = cipher.doFinal(ciphertextByte);
        return new String(decryptedtextByte);
    }
    private static String bytesToHex(byte[] bytes) {
        StringBuffer result = new StringBuffer();
        for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
        return result.toString();
    }

}

Источник PHP:

<?php

function encrypt($publicKeyBase64, $plaintext){
    $pub = base64_decode($publicKeyBase64);
    // public key conversion der to pem
    $pubPem = chunk_split(base64_encode($pub), 64, "\n");
    $pubPem = "-----BEGIN PUBLIC KEY-----\n" . $pubPem . "-----END PUBLIC KEY-----\n";
    $ublicKey = "";
    $publicKey = openssl_get_publickey($pubPem);
    if (!$publicKey) {
        echo "Cannot get public key" . "<br>";
    }
    $ciphertext = "";
    openssl_public_encrypt($plaintext, $ciphertext, $publicKey);
    if (!empty($ciphertext)) {
        openssl_free_key($publicKey);
        //echo "Encryption OK!" . "<br>";
    } else {
        echo "Cannot Encrypt" . "<br>";
    }
    $ciphertextBase64 = base64_encode($ciphertext);
    return $ciphertextBase64;
}

function decrypt($privateKeyBase64, $ciphertext){
    $pri = base64_decode($privateKeyBase64);
    // private key conversion der to pem
    $priPem = chunk_split(base64_encode($pri), 64, "\n");
    $priPem = "-----BEGIN PRIVATE KEY-----\n" . $priPem . "-----END PRIVATE KEY-----\n";
    $privateKey = openssl_get_privatekey($priPem);
    $Crypted = openssl_private_decrypt($ciphertext, $decryptedtext, $privateKey);
    if (!$Crypted) {
        echo 'Cannot Decrypt ' . openssl_error_string() . '<br>';
    } else {
        openssl_free_key($privateKey);
        //echo "decryptedtext: " . $decryptedtext . "<br>";
    }
    return $decryptedtext;
}

echo 'php version: ' . PHP_VERSION . ' openssl version: ' . OPENSSL_VERSION_TEXT . '<br>';
$plaintext = "this is the message to encrypt";
echo "plaintext: " . $plaintext . "<br>";

// RSA 512 keys from Java GenerateKeysSo.java
echo 'rsa encryption with original keys' . '<br>';
$priBase64 = "MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEA2wFgcni89ijJ/uijQkzCGF4JiUB1+mEJ48u4Lk0vxB7ym3/FCvOEnN2H7FLUzsGvXRhFriLBiSJlg2tOhV5eiwIDAQABAkEAkDpf4gNRrms+W/mpSshyKsoDTbh9+d5ePP601QlQI79lrsjdy2GLgk4RV1XmwYinM9Sk8G+ssyXTYHdby6A2wQIhAPcRtl6tub6PFiIE1jcuIkib/HzAdRYHZx3ZdzRTYDetAiEA4uv43xpGl5N8yG27Kv0DkRoOlr4Ch6oM24hLVw7ClhcCIFgdRAo+MQlqJH2bdf6WAHoez4x6YwepOjhmD2Jk/eK9AiEAtHgI6J5EEB56+gfS+CBa6tZ3Tcl1x6ElMp8Vk/ooJScCIQDUa3LUkcc58yjJYq8ZNQC/86+HIzd5MldTwg5buR1lpw==";
$pubBase64 = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANsBYHJ4vPYoyf7oo0JMwhheCYlAdfphCePLuC5NL8Qe8pt/xQrzhJzdh+xS1M7Br10YRa4iwYkiZYNrToVeXosCAwEAAQ==";
echo 'priBase64:' . $priBase64 . '<br>';
echo 'pubBase64:' . $pubBase64 . '<br>';
$ciphertextBase64 = encrypt($pubBase64, $plaintext);
echo 'ciphertext Base64:' . $ciphertextBase64 . '<br>';
$ciphertext = base64_decode($ciphertextBase64);
$decryptedtext = decrypt($priBase64, $ciphertext);
echo "decryptedtext: " . $decryptedtext . "<br><br>";

// keys created via modulus & exponent
$priBase64 = "MIGzAgEAMA0GCSqGSIb3DQEBAQUABIGeMIGbAgEAAkEA2wFgcni89ijJ/uijQkzCGF4JiUB1+mEJ48u4Lk0vxB7ym3/FCvOEnN2H7FLUzsGvXRhFriLBiSJlg2tOhV5eiwIBAAJBAJA6X+IDUa5rPlv5qUrIcirKA024ffneXjz+tNUJUCO/Za7I3cthi4JOEVdV5sGIpzPUpPBvrLMl02B3W8ugNsECAQACAQACAQACAQACAQA=";
$pubBase64 = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANsBYHJ4vPYoyf7oo0JMwhheCYlAdfphCePLuC5NL8Qe8pt/xQrzhJzdh+xS1M7Br10YRa4iwYkiZYNrToVeXosCAwEAAQ==";
echo 'rsa encryption with keys created via modulus & exponents' . '<br>';
echo 'priBase64:' . $priBase64 . '<br>';
echo 'pubBase64:' . $pubBase64 . '<br>';
$ciphertextBase64 = encrypt($pubBase64, $plaintext);
echo 'ciphertext Base64:' . $ciphertextBase64 . '<br>';
$ciphertext = base64_decode($ciphertextBase64);
$decryptedtext = decrypt($priBase64, $ciphertext);
echo "decryptedtext: " . $decryptedtext . "<br><br>";
echo 'decrypt error: error:0909006C:PEM routines:get_name:no start line' . '<br>';
?>

Окончательное редактирование и вывод

Если мы хотим использовать пару ключей RSA private-publi c для шифрования (а также подписи? ) в других системах, кроме Java, важно, чтобы закрытый ключ сохранялся немедленно. Если мы пытаемся восстановить закрытый ключ из закодированной формы (через PKCS8EncodedKeySpe c), некоторые данные определенно отсутствуют. Эти перестроенные закрытые ключи не будут работать (здесь, в PHP / openssl).

Если нам нужно перестроить закрытый ключ из закодированной формы (byte []), ключи должны быть получить улучшение с помощью метода под названием "createCrtKey" - этот метод был написан президентом Джеймсом К. Полком и всем ему go. Поскольку ссылки иногда устаревают, я пометил свой ответ ниже как принятый, потому что там задокументирован метод createCrtKey.

Спасибо @President James K. Polk, @Topaco и @michalk за то, что они направили меня в правильном направлении .

Ответы [ 2 ]

1 голос
/ 21 июня 2020

Минимальная информация, необходимая для выполнения операции дешифрования RSA, - это модуль n и показатель степени расшифровки d. Существует оптимизация, которая может быть применена к расшифровке RSA с использованием китайской теоремы об остатках, при которой возведение в степень выполняется отдельно для простых чисел RSA, а затем объединяется для получения окончательного значения, и, таким образом, для этой цели в RSA есть несколько дополнительных полей. Синтаксис закрытого ключа и интерфейс Java RSAPrivateCrtKey, смоделированный на его основе.

Теперь возникает вопрос: когда два экземпляра RSAPrivateCrtKey равны? Я бы сказал, что они равны, если они одинаково функционируют в алгоритме RSA. Вы просите более узкого определения, а именно: они равны, когда равны их закодированные формы. Проблема с этим определением в том, что оно слишком зависит от реализации c. В настоящее время , когда провайдер «Sun» генерирует пару ключей, он всегда упорядочивает простые числа p и q так, чтобы p> q. Но мне нравится другой вариант, где p <<code>q. Интерфейсу RSAPrivateCrtKey все равно, так как он не проверяет. В документации Javadoc для интерфейса не указан порядок. Вы можете изменить мой код, чтобы получить то, что должно иметь ту же закодированную форму, что и реализация current"Sun", просто изменив сравнение в p.compareTo(q) > 0. Однако реализация по умолчанию может измениться в соответствии с моими предпочтениями в будущем, и это произойдет, если мой план по захвату мира будет успешным. Документация Javadoc является спецификацией, и реализация может изменяться, если она соответствует документации Javadoc.

Ниже я представил реализацию функции равенства, в которую я попытался включить как можно более широкое понятие согласованного равенства со спецификацией. То есть любые два экземпляра RSAPrivateCRTKey, для которых keyEquals возвращает true, должны давать идентичные результаты при использовании в алгоритме RSA, а если возвращается false, то должно быть по крайней мере одно значение, для которого они дают разные результаты.

public static boolean keyEquals(RSAPrivateCrtKey k1, RSAPrivateCrtKey k2) {

    final BigInteger ZERO = BigInteger.ZERO;

    boolean result = true;

    result = result && isConsistent(k1) && isConsistent(k2);
    result = result && k1.getModulus().equals(k2.getModulus());
    BigInteger lambda = computeCarmichaelLambda(k1.getPrimeP(), k1.getPrimeQ());

    result = result && k1.getPublicExponent().subtract(k2.getPublicExponent()).mod(lambda).equals(ZERO);
    result = result && k1.getPrivateExponent().subtract(k2.getPrivateExponent()).mod(lambda).equals(ZERO);

    return result;
}

private static boolean isConsistent(RSAPrivateCrtKey k1) {
    final BigInteger ZERO = BigInteger.ZERO;
    final BigInteger ONE = BigInteger.ONE;

    BigInteger n = k1.getModulus();
    BigInteger p = k1.getPrimeP();
    BigInteger q = k1.getPrimeQ();
    BigInteger e = k1.getPublicExponent();
    BigInteger d = k1.getPrivateExponent();

    boolean result = true;

    result = p.multiply(q).equals(n);
    BigInteger lambda = computeCarmichaelLambda(p, q);
    result = result && e.multiply(d).mod(lambda).equals(ONE);
    result = result && d.subtract(key.getPrimeExponentP()).mod(p.subtract(ONE)).equals(ZERO);
    result = result && d.subtract(key.getPrimeExponentQ()).mod(q.subtract(ONE)).equals(ZERO);
    result = result && q.multiply(k1.getCrtCoefficient()).mod(p).equals(ONE);
    return result;
}

private static BigInteger computeCarmichaelLambda(BigInteger p, BigInteger q) {
    return lcm(p.subtract(BigInteger.ONE), q.subtract(BigInteger.ONE));
}

private static BigInteger lcm(BigInteger x, BigInteger y) {
    return x.multiply(y).divide(x.gcd(y));
}
0 голосов
/ 21 июня 2020

Это измененная версия моей программы, которая имеет дополнительный код от @President James K. Polk (см. Ссылку Topaco выше). Даже если восстановленный закрытый ключ CRT теперь длиннее закрытого ключа восстановления, он не соответствует исходному (закодированному) закрытому ключу. Поскольку я использую закодированные частные ключи и ключи publi c для шифрования / дешифрования PHP RSA, есть забавный факт, что исходные ключи работают успешно, а восстановленные - нет ...

В этой версии используется длина ключа 512 бит, что небезопасно только для демонстрации (чтобы ключи были короче).

результат:

Rebuilding of a RSA PrivateKey from modulus & exponent
privateKey equals rebuild: false
publicKey equals rebuild: true
privateKey original    encoded: 30820154020100300d06092a864886f70d01010105000482013e3082013a020100024100a45477b9f00f51c8e1d5cb961a485c74ee123aa6da5c5bfd43f62acee9b684a8f140bb7a68996a77d04bdaabc5f259cb38a7bef909f4d85c6a597519a09aec9b0203010001024066ea4fa12f6b28b93a567f0e1e9fbae7b041d261b4d7aaf4ce9f58e8050ebdbd5e2a6261f06de2d72c4fdc6a62465f9cad9e8f5860bb2f8395cd903a214fb441022100e3b260dcced139557591b609470d8f0e518351a97bdbf26a59a41140a68778e9022100b8c1ab98f7b7280bd4b53fa3ed09c11d12aec9873d8a4a05e43152bc0d3346e302201d801ff29bcd19bb8bc6fc29c98de529fabfa3d5ec993b9831d302f5385e36f90220009e0d0fbecc2ae3173bdfd1916a35edfdf0fd95691c3c3116d91f58a786a357022100a810110da3d9d4de34e64029a3535368bb52e7b81055239cb4443d5172aea8e5
privateKey rebuild     encoded: 3081b2020100300d06092a864886f70d010101050004819d30819a020100024100a45477b9f00f51c8e1d5cb961a485c74ee123aa6da5c5bfd43f62acee9b684a8f140bb7a68996a77d04bdaabc5f259cb38a7bef909f4d85c6a597519a09aec9b020100024066ea4fa12f6b28b93a567f0e1e9fbae7b041d261b4d7aaf4ce9f58e8050ebdbd5e2a6261f06de2d72c4fdc6a62465f9cad9e8f5860bb2f8395cd903a214fb441020100020100020100020100020100
privateKey rebuild CRT encoded: 30820153020100300d06092a864886f70d01010105000482013d30820139020100024100a45477b9f00f51c8e1d5cb961a485c74ee123aa6da5c5bfd43f62acee9b684a8f140bb7a68996a77d04bdaabc5f259cb38a7bef909f4d85c6a597519a09aec9b0203010001024066ea4fa12f6b28b93a567f0e1e9fbae7b041d261b4d7aaf4ce9f58e8050ebdbd5e2a6261f06de2d72c4fdc6a62465f9cad9e8f5860bb2f8395cd903a214fb441022100b8c1ab98f7b7280bd4b53fa3ed09c11d12aec9873d8a4a05e43152bc0d3346e3022100e3b260dcced139557591b609470d8f0e518351a97bdbf26a59a41140a68778e90220009e0d0fbecc2ae3173bdfd1916a35edfdf0fd95691c3c3116d91f58a786a35702201d801ff29bcd19bb8bc6fc29c98de529fabfa3d5ec993b9831d302f5385e36f9022030634f5490e1bb4b56a68715d3c80a92c6e8f7c9f3e79f125a9969e6fc095705

код:

import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Arrays;

public class RebuildRSAPrivateKey2 {
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException {
        System.out.println("Rebuilding of a RSA PrivateKey from modulus & exponent");
        // rsa key generation
        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
        //kpGen.initialize(2048, new SecureRandom());
        kpGen.initialize(512, new SecureRandom()); // don't use 512 bit keys as they are insecure !!
        KeyPair keyPair = kpGen.generateKeyPair();
        // private key
        PrivateKey privateKey = keyPair.getPrivate();
        // get modulus & exponent
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
        BigInteger modulus = rsaPrivateKey.getModulus();
        BigInteger privateExponent = rsaPrivateKey.getPrivateExponent();
        // rebuild the private key
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(modulus, privateExponent);
        PrivateKey privateKeyRebuild = keyFactory.generatePrivate(rsaPrivateKeySpec);
        System.out.println("privateKey equals rebuild: " + Arrays.equals(privateKey.getEncoded(), privateKeyRebuild.getEncoded()));
        // public key
        PublicKey publicKey = keyPair.getPublic();
        // get modulus & exponent
        RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
        BigInteger modulusPub = rsaPublicKey.getModulus();
        BigInteger publicExponent = rsaPublicKey.getPublicExponent();
        // rebuild the public key
        KeyFactory keyFactoryPub = KeyFactory.getInstance("RSA");
        RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(modulusPub, publicExponent);
        PublicKey publicKeyRebuild = keyFactory.generatePublic(rsaPublicKeySpec);
        System.out.println("publicKey equals rebuild: " + Arrays.equals(publicKey.getEncoded(), publicKeyRebuild.getEncoded()));
        System.out.println("\nprivateKey original    encoded: " + bytesToHex(privateKey.getEncoded()));
        System.out.println("privateKey rebuild     encoded: " + bytesToHex(privateKeyRebuild.getEncoded()));
        RSAPrivateKey rsaPrivateKeyRebuild = (RSAPrivateKey) privateKeyRebuild;
        RSAPublicKey rsaPublicKeyRebuild = (RSAPublicKey) publicKeyRebuild;
        RSAPrivateCrtKey rsaPrivateCrtKey = createCrtKey(rsaPublicKeyRebuild, rsaPrivateKeyRebuild);
        System.out.println("privateKey rebuild CRT encoded: " + bytesToHex(rsaPrivateCrtKey.getEncoded()));
    }

    /**
     * https://stackoverflow.com/questions/43136036/how-to-get-a-rsaprivatecrtkey-from-a-rsaprivatekey
     * answered Mar 31 '17 at 18:16 President James K. Polk
     * Find a factor of n by following the algorithm outlined in Handbook of Applied Cryptography, section
     * 8.2.2(i). See http://cacr.uwaterloo.ca/hac/about/chap8.pdf.
     *
     */

    private static BigInteger findFactor(BigInteger e, BigInteger d, BigInteger n) {
        BigInteger edMinus1 = e.multiply(d).subtract(BigInteger.ONE);
        int s = edMinus1.getLowestSetBit();
        BigInteger t = edMinus1.shiftRight(s);

        for (int aInt = 2; true; aInt++) {
            BigInteger aPow = BigInteger.valueOf(aInt).modPow(t, n);
            for (int i = 1; i <= s; i++) {
                if (aPow.equals(BigInteger.ONE)) {
                    break;
                }
                if (aPow.equals(n.subtract(BigInteger.ONE))) {
                    break;
                }
                BigInteger aPowSquared = aPow.multiply(aPow).mod(n);
                if (aPowSquared.equals(BigInteger.ONE)) {
                    return aPow.subtract(BigInteger.ONE).gcd(n);
                }
                aPow = aPowSquared;
            }
        }
    }

    public static RSAPrivateCrtKey createCrtKey(RSAPublicKey rsaPub, RSAPrivateKey rsaPriv) throws NoSuchAlgorithmException, InvalidKeySpecException {
        BigInteger e = rsaPub.getPublicExponent();
        BigInteger d = rsaPriv.getPrivateExponent();
        BigInteger n = rsaPub.getModulus();
        BigInteger p = findFactor(e, d, n);
        BigInteger q = n.divide(p);
        if (p.compareTo(q) > 0) {
            BigInteger t = p;
            p = q;
            q = t;
        }
        BigInteger exp1 = d.mod(p.subtract(BigInteger.ONE));
        BigInteger exp2 = d.mod(q.subtract(BigInteger.ONE));
        BigInteger coeff = q.modInverse(p);
        RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(n, e, d, p, q, exp1, exp2, coeff);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return (RSAPrivateCrtKey) kf.generatePrivate(keySpec);
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuffer result = new StringBuffer();
        for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
        return result.toString();
    }
}
...