[英]Rebuild of a RSA Private Key from modulus & exponent fails
我正在嘗試從模數和私有/公共指數重建 RSA 密鑰對。 轉換對公鑰有效,但在比較編碼的私鑰時無法轉換為私鑰。
當使用此重建私鑰/公鑰對進行加密時,它在 Java 中有效(,),但在 PHP 中使用重建密鑰對時,解密部分失敗(加密正在工作)。 所以在我看來,重建私鑰與“原始”私鑰不同。
僅供參考:使用“原始”密鑰對在 PHP 中一切正常。
所以我的問題是:如何從(BigInteger)模數和私有指數中檢索“原始”私鑰?
編輯:最后看我的最終編輯
我的示例代碼顯示了公鑰與重建一個的相等性,並且私鑰是不同的:
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 私鑰/公鑰對可以恢復,並且加密和解密在 Java 和 PHP 中工作。 密鑰是不安全的 RSA 512 位密鑰和 Base64 解碼。
然后從模數和私有/公共指數派生相同的密鑰,並且加密/解密在 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>';
?>
最終編輯解決方案和結論
如果我們想在 Java 之外的其他系統中使用 RSA 私鑰-公鑰對進行加密(以及簽名?),重要的是立即保存私鑰。 如果我們試圖從編碼形式(通過 PKCS8EncodedKeySpec)重建私鑰,肯定會丟失一些數據。 那些重建私鑰將無法工作(在 PHP/openssl 中)。
如果我們需要從編碼形式 (byte[]) 重建私鑰,則需要通過稱為“createCrtKey”的方法來增強密鑰 - 該方法由 James K. Polk 總統編寫,所有功勞歸於他。 由於鏈接有時會過期,因此我在下面將我自己的答案標記為已接受的答案,因為那里記錄了 createCrtKey 方法。
感謝@President James K. Polk、@Topaco 和 @michalk 引導我走向正確的方向。
執行 RSA 解密操作所需的最少信息是模數n
和解密指數d
。 有一個優化可以應用於涉及中國剩余定理的 RSA 解密,其中求冪是分別對 RSA 素數進行模數,然后組合產生最終值,因此在RSA 私鑰語法中有一些用於此目的的額外字段字段以及仿照它的 Java RSAPrivateCrtKey
接口。
現在這里提出的問題是:兩個 RSAPrivateCrtKey 實例何時相等? 我認為當它們在 RSA 算法中相同時,它們是相等的。 您要求更窄的定義,即當它們的編碼 forms 相等時它們相等。 這個定義的問題在於它過於特定於實現。 目前,當“Sun”提供者生成一個密鑰對時,它總是對素數p
和q
進行排序,使得p
> q
。 但我喜歡另一種方式, p
< q
。 RSAPrivateCrtKey 接口不關心任何一種方式,因為它不進行檢查。 接口的 Javadocs 沒有指定順序。 您可以通過反轉p.compareTo(q) > 0
中的比較來更改我的代碼以生成與當前“Sun”實現相同的編碼形式。 但是,默認實現可以在將來更改以匹配我的偏好,如果我接管世界的計划成功的話。 Javadocs 是規范,只要符合 Javadocs,實現可能會發生變化。
下面我提供了一個等式 function 的實現,我試圖在其中包含與規范一致的最廣泛的等式概念。 也就是說, keyEquals
返回true
的任何兩個 RSAPrivateCRTKey 實例在 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));
}
這是我的程序的修改版本,其中包含來自@President James K. Polk 的附加代碼(參見上面的 Topaco 鏈接)。 即使重建 CRT 私鑰現在比重建私鑰長,它也不匹配原始(編碼)私鑰。 當我使用 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();
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.