I am trying to generate an encrypted message to send to a remote server and when I do it in Java I succeed but when I try it in PHP, I fail.
I would like your help in pointing out where I am going wrong in the php version. Below is the Java and php version of my code.
private String encryptMessage() {
String msg = "";
String message = "this is the message to send";
String modulus = "xxxxxxxxxxxxxxx";
String exponent = "111";
Security.addProvider(new BouncyCastleProvider());
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(new BigInteger(modulus, 16), new BigInteger(exponent, 16));
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey publicKey = factory.generatePublic(publicKeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] messageDataBytes = cipher.doFinal(message.getBytes("UTF8"));
msg = Base64.getEncoder().encodeToString(messageDataBytes).replaceAll("\\r|\\n", "");
return msg;
}
The php version that fails
private function encryptMessage()
{
$message = "this is the message to send";
$modulus = "xxxxxxxxxxxxxxx";
$exponent = "111";
$rsa = new RSA();
$exponent = new BigInteger( $exponent, 16);
$modulus = new BigInteger($modulus, 16);
$rsa->loadKey(['n' => $modulus, 'e'=>$exponent]);
$rsa->setPublicKey();
$publicKey = $rsa->getPublicKey();
openssl_public_encrypt($message, $encryptedData, $publicKey);
return base64_encode($encryptedData);
}
For Java,I am using the Bouncy Castle Library while I am using phpseclib version 2 library.
Thanks.
Result from the Java method: VU06XTAFfNfBSjhWz+deqVt/cZw7YBG1nfl0g/c8GwInVTiW1oOwX53z2+Sxp3o65kNztwRSA4ARVKjnrH4M/ygJ+9jrHLvKWt2mmKbcTF9tkmd3Ezdd429s44azulFNRwH0lqQNy80mPTpNzRN4QDkeuRp1mUqDlNHQ70T7BdWwCvjMXZ/kBjXEHocYE+fNmXjgxCQFDCkek1CZQ+IDvxRyW5MtsuQDzAphUO3Kj5epMTJkzUuekV1RzCUErerfUUb83HAo3hh6IGuqhSw3MKjVY7g7AlO0P/0n1pI2+fi/g0EojK7MJt8mzx/WrWxQH2fiMo0GuTb4+YB4+YS9dQ==
Result from PHP:
U1pWl/ohAkP+1+L25vCMwVeQ3bkRJwwWocDn/lWgIkFYM6V8/Wn+LHVbm/ku0ZV0we1tXUkQE784xH7q7Cd9h4P2mGA5XWHdtGHTzQaxHgT31DN/60I5rDsC8A3OJORNbJk/6rBh8PuDEi33vqtayE7v4rqIYXTxAwDWUgjvnBtJOQtZXgfhrnCb/jKiLP5WY2yBfa5/3eHoLCtmButGy2GgrEmg08OnvJlqSTQd/Xtc+1NhWVlha8+vk2e/rZZn/aOzvYAOoUZQbWRUhlonObfom/1HtRJrBJGfJxv2zLFNPP6RrDgoYptMhsNs4r8OVSxj6qiEYFx9tjNqp9c1xA==
My solution consists of 3 files to get a complete testing environment with full working encryption and decryption in Java
and encryption only in PHP.
The test environment I'm working on is on Java-side 11.0.6+8-b520.43 (without any dependencies, e.g. Bouncy Castle)
and on PHP-side php version 7.4.6, openssl version: OpenSSL 1.1.1g 21 Apr 2020 and phpseclib 2.0.27.
GenerateKeysSo.java: generates an RSA private/public keypair in modulus/exponent-presentation. I use the insecure 512 bit keylength just to keep the string length a little bit shorter - in production you should use a minimum of 2048 bit keylength.
import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Base64;
public class GenerateKeysSo {
public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
System.out.println("https://stackoverflow.com/questions/53947613/rsa-public-key-generated-in-java-is-not-valid-in-php");
System.out.println("Generate RSA keys");
System.out.println("java version: " + Runtime.version());
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
//kpGen.initialize(2048, new SecureRandom());
kpGen.initialize(512, new SecureRandom()); // don't use 512 bitlength as it is insecure !
KeyPair keyPair = kpGen.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
byte[] privateKeyEncoded = privateKey.getEncoded();
byte[] publicKeyEncoded = publicKey.getEncoded();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
BigInteger modulus = rsaPublicKey.getModulus();
BigInteger privateExponent = rsaPrivateKey.getPrivateExponent();
BigInteger publicKeyExponent = rsaPublicKey.getPublicExponent();
System.out.println("privateKey Base64: " + Base64.getEncoder().encodeToString(privateKeyEncoded));
System.out.println("publicKey Base64: " + Base64.getEncoder().encodeToString(publicKeyEncoded));
System.out.println("modulus : " + modulus);
System.out.println("privateExponent : " + privateExponent);
System.out.println("publicExponent : " + publicKeyExponent);
}
}
The result is short and you need to copy the 3 values of modulus, privateExponent and publicExponent with copy/paste to EncryptSo.java and to
EncryptSo.php:
Generate RSA keys
java version: 11.0.6+8-b520.43
privateKey Base64: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAgKpP+4e9D3r5Mbw4VBaQQG/LRw7nqDOWOU6p+teU2c+mTPE2apPzYUku9AaNf+BiPyjzmkWh/9d7E5jUDWfS9QIDAQABAkA9Ia5y0eFwE3uGt+kDetz64KLylhBXCHudqtR0TC169s4UfGr6EquaqX9YKmQ4ApC5mkO65Rha8V0WzV6PEUlBAiEA6XtDObkbbhAMaDXpLkzqu0I5O2e3cpTMGwCTaK/0GVkCIQCNExoakOZ1+0f5UuiztmGo7UqBpZEieiybL25H/iI2/QIhANPubzcWLy/YoWp2hRLh/q+7jPe/TWVUa3CXNTbxgEEBAiAyA22jsW8isJ0JlQ0OeUASJV3erJXRhUK/GYedWklC+QIgLnxzZVDSNNEGYFpUER0v2bIFWqVXlnm87y5LRbzWn/I=
publicKey Base64: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAICqT/uHvQ96+TG8OFQWkEBvy0cO56gzljlOqfrXlNnPpkzxNmqT82FJLvQGjX/gYj8o85pFof/XexOY1A1n0vUCAwEAAQ==
modulus : 6738747659637003090018178263545882875809887577706500222853194115214616163418986509701683514500084328991941041539147237736316411294805049495944982937719541
privateExponent : 3201720015041533823900789397901821886674218828976379815054133670438877551559197501362784700723343540531608097210070472157677675484894343978127401531033921
publicExponent : 65537
File 2 is the encryption/decryption routine in Java (EncryptSo.java). Paste the 3 values from GenerateSo to the three string variables modulusString,
privateExponentString and publicExponentString. Run the program and it will encrypt a message and decrypt it. After running the PHP-encryption (file 3)
you have an output there - you can paste this output to line 42 (variable ciphertextFromPhp). This string will get decrypted as well.
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.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Base64;
public class EncryptSo {
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchPaddingException, IOException {
System.out.println("https://stackoverflow.com/questions/61966418/converting-a-java-encryption-method-to-php");
System.out.println("encrypt and decrypt with Java with key generation via modulus and exponent");
String modulusString = "7655411376708364139873444642436134423578239360576333940175884244078181837546293928220019721607335417517025652060442851820596480386882812189086919617782503";
String privateExponentString = "6181379361775076881056852519774102589787650800051550108278185489542231510128103539355646657479306697213227070361180012317478947328351699562861534356668225";
String publicExponentString = "65537";
BigInteger modulusBI = new BigInteger(modulusString);
BigInteger publicExponentBI = new BigInteger(publicExponentString,10); // 10 = numeric string
BigInteger privateExponentBI = new BigInteger(privateExponentString,10); // 16 = hex string
// generate private & public keys
KeyFactory factory = KeyFactory.getInstance("RSA");
RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(modulusBI, privateExponentBI);
PrivateKey privateKeyBI = factory.generatePrivate(rsaPrivateKeySpec);
RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(modulusBI, publicExponentBI);
PublicKey publicKeyBI = factory.generatePublic(rsaPublicKeySpec);
// encryption
String message = "this is the message to send";
System.out.println("message Original : " + message);
String ciphertext = encryptMessageEnc(message, publicKeyBI);
System.out.println("message encrypted: " + ciphertext);
// decryption
String decryptedtext = decryptMessageEnc(ciphertext, privateKeyBI);
System.out.println("message decrypted: " + decryptedtext);
System.out.println("decryption of a encrypted message from PHP");
String ciphertextFromPhp = "B3a1v8oGVYqRpRwRwGP5KRaqaNiiXFRfTdWnz0LsI4Ek63BpXIrWsEXsVNaj1jSu4XIQNrvYHIpwdxwKQrBUUfXXwq6Pix0YfuOmz7mUJMyTerHMcVzcXIpEGAD/L0gGomfCbkahed/CimThPSO527ulazHVirOY173JjMTrWdqyFLS6L7sCD6UHkU49gW94ADkOJtRXOiIWTg==";
System.out.println("message encrypted: " + ciphertextFromPhp);
decryptedtext = decryptMessageEnc(ciphertext, privateKeyBI);
System.out.println("message decrypted: " + decryptedtext);
}
private static String encryptMessageEnc(String message, PublicKey publicKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IOException, BadPaddingException, IllegalBlockSizeException {
String msg = "";
//Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] ciphertextBytes = cipher.doFinal(message.getBytes("UTF8"));
msg = Base64.getEncoder().encodeToString(ciphertextBytes).replaceAll("\\r|\\n", "");
return msg;
}
private static String decryptMessageEnc(String msg, PrivateKey privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
//Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] message = Base64.getDecoder().decode(msg);
byte[] messageDataBytes = cipher.doFinal(message);
return new String(messageDataBytes);
}
}
output of EncryptSo:
encrypt and decrypt with Java with key generation via modulus and exponent
java version: 11.0.6+8-b520.43
message Original : this is the message to send
message encrypted: WZs0jp6yMWR71I4g7Oxo54A28yDZRBs5usKYLz9NfoRkMPy7vpe+riLSZzr6mdC8XZFNGveH9HsG2maZWSxhsw==
message decrypted: this is the message to send
decryption of a encrypted message from PHP
message encrypted: B3a1v8oGVYqRpRwRwGP5KRaqaNiiXFRfTdWnz0LsI4Ek63BpXIrWsEXsVNaj1jSu4XIQNrvYHIpwdxwKQrBUUfXXwq6Pix0YfuOmz7mUJMyTerHMcVzcXIpEGAD/L0gGomfCbkahed/CimThPSO527ulazHVirOY173JjMTrWdqyFLS6L7sCD6UHkU49gW94ADkOJtRXOiIWTg==
message decrypted: this is the message to send
File 3 (EncryptSo.php) is simple PHP-application that builds the public key via modulus and exponent and need the external dependency "phpseclib". I included the library without any composer-help just manually. Paste the modulus and exponent to the variables modulusString and publicExponentString and let it run.
<?php
// https://stackoverflow.com/questions/61966418/converting-a-java-encryption-method-to-php
include('phpseclib\Math\BigInteger.php');
include('phpseclib\Crypt\RSA.php');
include('phpseclib\Crypt\HASH.php');
echo 'php version: ' . PHP_VERSION . ' openssl version: ' . OPENSSL_VERSION_TEXT . ' phpseclib 2.0.27' . PHP_EOL;
$message = "this is the message to send";
$modulusString = "7655411376708364139873444642436134423578239360576333940175884244078181837546293928220019721607335417517025652060442851820596480386882812189086919617782503";
$publicExponentString = "65537";
$rsa = new \phpseclib\Crypt\RSA();
//$rsa = new Crypt_RSA();
$exponent = new phpseclib\Math\BigInteger($publicExponentString, 256);
$modulus = new phpseclib\Math\BigInteger($modulusString, 256);
$PUBLICKEY = "";
$rsa->loadKey(['n' => $modulus, 'e'=>$exponent]);
$rsa->setPublicKey();
$publicKey = $rsa->getPublicKey();
openssl_public_encrypt($message, $encryptedData, $publicKey);
$encryptedData = base64_encode($encryptedData);
echo PHP_EOL ."encryptedData base64: " . $encryptedData . PHP_EOL;
// encryptedData base64: K4DCBvi2lWbyQJGiQbZ9cXiZy49sg+Q0KKPvj7+TMObTCyWKtoQce8pmUJlDZ48+K1Fy3vHtTTZFDQzr7fkWKoeYffl171IsxOgFLz2bmjQYhrNiLy5ZI03qUX4i9MI0mtSMlITXhiryDOajHB61Cgs5cfgH+yVCJItV7cM/qMBAGnQVqoMxRAKaSOBEz/zxp3If5X9Jb5zuhA==
?>
Result of PHP encryption:
php version: 7.4.6 openssl version: OpenSSL 1.1.1g 21 Apr 2020 phpseclib 2.0.27
encryptedData base64: M5ys/5JrlleQO5MJDVl6FvRcIUIcxXQhp00y/KLHfB1p0XRNaVaWRwV9t5NKoJmo4wKI1KQI1H9PBd71c6t3i7IWsWcS8QsebOk5iitacvgOTJBW71pjrzWkv4+M7Ge8KZ/jhCl6nQ+e9zNSkJf4HZE1NHmOzFan3pBN1gXDptj19AZPue/MxqdAIgDD538UElvhTzyWMpCJeA==
Related
Small question regarding a Java code for ECC please.
I am having a very simple piece of Java code. (runnable, just truncating the keys with ...] )
import javax.crypto.Cipher;
import javax.crypto.NullCipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class ECTest {
public static void main(String[] args){
try {
String mySecretMessage = "MySecretMessage";
System.out.println("Step 1: This is the secret message I want to encrypt using ECC -> " + mySecretMessage);
String mySecretMessageEncrypted = encrypt(mySecretMessage.getBytes(StandardCharsets.UTF_8), "MFkwEwYHKoZIzj0CA[...]rBlZtvVpKyQdHxYZ0W6a/IixWc0WjDqqcVAtrwCILmHU7Q==");
System.out.println("Step 2: This is the secret message encrypted, I can share this -> " + mySecretMessageEncrypted);
String mySecretMessageDecrypted = decrypt(mySecretMessageEncrypted.getBytes(StandardCharsets.UTF_8), "MEECAQAwEwYHKoZIzj0[...]RnYMh52QmDg==");
System.out.println("Step 3: This is the secret message decrypted, it should be equal to mySecretMessage, but not mySecretMessageEncrypted -> " + mySecretMessageDecrypted);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static String encrypt(byte[] data, String key) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(key);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("EC");
ECPublicKey pubKey = (ECPublicKey) keyFactory.generatePublic(x509KeySpec);
ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(pubKey.getW(), pubKey.getParams());
Cipher cipher = new NullCipher();
cipher.init(Cipher.ENCRYPT_MODE, pubKey, ecPublicKeySpec.getParams());
return new String(cipher.doFinal(data), StandardCharsets.UTF_8);
}
public static String decrypt(byte[] data, String key) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(key);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("EC");
ECPrivateKey priKey = (ECPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(priKey.getS(), priKey.getParams());
Cipher cipher = new NullCipher();
cipher.init(Cipher.DECRYPT_MODE, priKey, ecPrivateKeySpec.getParams());
return new String(cipher.doFinal(data), StandardCharsets.UTF_8);
}
}
And here is the output:
Step 1: This is the secret message I want to encrypt using ECC -> MySecretMessage
Step 2: This is the secret message encrypted, I can share this -> MySecretMessage
Step 3: This is the secret message decrypted, it should be equal to mySecretMessage, but not mySecretMessageEncrypted -> MySecretMessage
The issue I am facing is on Step 2: I am surprisingly getting "MySecretMessage", same as the original and decrypted message.
I was hoping to see something encrypted here.
May I ask what is the technical issue please?
Thank you
This question already has an answer here:
RSA encyrption - converting between bytes array and String [duplicate]
(1 answer)
Closed 2 years ago.
we are trying RSA Encryption & Decryption and the issue happens while decrypting. This is our decryption code
Cipher oaepFromInit = Cipher.getInstance("RSA/ECB/OAEPPadding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-1", "MGF1", new MGF1ParameterSpec("SHA-1"), PSpecified.DEFAULT);
oaepFromInit.init(Cipher.DECRYPT_MODE, rsaPrivateKey, oaepParams);
byte[] dec = oaepFromInit.doFinal(encrytpedData.getBytes());
The encrytpedData is like this
s11Pyj5rrVOfOiWtxpGq+K5D+pYi16CyyX/EwKfMErBkHJ4aVlTmnhrfeCS7LEeXgTs3gkFp96I/oTedG/rXxF2hTAmMH40k0joKJbRtzO858/0dcaaE1uNzr/rI0Jj3ebXPLGhefCMNNpyFH5V4ukVo6vtev5Z9U8oNkUQolbX/r5jJJomkKCCnzGoHMdQg5dafj9Sw/qakO13501YBrkxS0i9ca0GZ8Ll42NwkOZuInh+MAu+gYW4vAr284eJsqgLgTp0+MS1tmfwR6EXgspk1nYR/U84P3MBZAdpmD3nxsVV3iVOCUeoqVyd4kw7M2pvXev6hMbMN4P1nnomo8g==
An error exception was thrown saying javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes
We looked into this link getting a IllegalBlockSizeException: Data must not be longer than 256 bytes when using rsa, but the issue was with the way we were feeding the encrytpedData.
This is a code we got online
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();
RSAPublicKey pubkey = (RSAPublicKey) kp.getPublic();
RSAPrivateKey privkey = (RSAPrivateKey) kp.getPrivate();
// --- encrypt given algorithm string
Cipher oaepFromAlgo = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
oaepFromAlgo.init(Cipher.ENCRYPT_MODE, pubkey);
byte[] ct = oaepFromAlgo.doFinal("chakka".getBytes(StandardCharsets.UTF_8));
// --- decrypt given OAEPParameterSpec
Cipher oaepFromInit = Cipher.getInstance("RSA/ECB/OAEPPadding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-1", "MGF1", new MGF1ParameterSpec("SHA-1"), PSpecified.DEFAULT);
oaepFromInit.init(Cipher.DECRYPT_MODE, privkey, oaepParams);
byte[] pt = oaepFromInit.doFinal(ct);
System.out.println("Printing decoded string");
System.out.println(new String(pt, StandardCharsets.UTF_8));
Here they are supplying the encrypted array directly and works fine, but in our case we are getting encrytpedData as String. Both the codes are same but the supply of encrypted data differs.
byte[] dec = oaepFromInit.doFinal(encrytpedData.getBytes());
This is where we are getting the exception, so we tried converting the encrytpedData to byte[] in a different way via these two links
RSA encyrption - converting between bytes array and String [duplicate]
IllegalBlockSizeException when trying to encrypt and decrypt a string with AES
So we tried like this
byte[] dec = oaepFromInit.doFinal(Base64.getDecoder().decode(encrytpedData)); which resulted in this javax.crypto.BadPaddingException: Message is larger than modulus.
byte[] dec = oaepFromInit.doFinal(encrytpedData.getBytes(StandardCharsets.UTF_8)); which gave the same old exception javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes.
Any help would be appreciated
The below full example code is taken from my private cross-platform project and should work. It encrypts a string using RSA encryption with a 2048 key pair and uses OAEP padding with SHA-1 as hash.
The encrypted data are in base64 encoding. For demonstration purpose I'm using static (hardcoded) keys in PEM-format - never ever do this in production.
You can run the code online here: https://repl.it/#javacrypto/CpcJavaRsaEncryptionOaepSha1String
this is the output:
RSA 2048 encryption OAEP SHA-1 string
plaintext: The quick brown fox jumps over the lazy dog
* * * encrypt the plaintext with the RSA public key * * *
ciphertextBase64: pSPayOK79NMPLpaQOp8L7dOTdLeS+jVU5cKPF5mDOvuKtnOjf/NYfgsf1JWa5Ud/fzsXrSN8I/8KAs/freagOYflv6PGUHZ7cYk1iX6sO5cmdD0Mfglj39NxczbqCK3FG20hQfa01ZaOu/dV8+QvVx851ph1nEl8arNML5ohhjIdZTxR3olGouGzDJmuJkDlv2fJICP7sdMnvl7t21QI701I2xR01eatD5src5fY//EIOEjSoTesCBZwEUz1S3UpclNqGONAqaYqlscdTyAlY3Hg8smJRqKWk7ZWKSTYsocBbFeTvcNy75LG/xyILM0l4U+NnwGUypkjR2vJRGVQtw==
* * * decrypt the ciphertext with the RSA private key * * *
ciphertextReceivedBase64: pSPayOK79NMPLpaQOp8L7dOTdLeS+jVU5cKPF5mDOvuKtnOjf/NYfgsf1JWa5Ud/fzsXrSN8I/8KAs/freagOYflv6PGUHZ7cYk1iX6sO5cmdD0Mfglj39NxczbqCK3FG20hQfa01ZaOu/dV8+QvVx851ph1nEl8arNML5ohhjIdZTxR3olGouGzDJmuJkDlv2fJICP7sdMnvl7t21QI701I2xR01eatD5src5fY//EIOEjSoTesCBZwEUz1S3UpclNqGONAqaYqlscdTyAlY3Hg8smJRqKWk7ZWKSTYsocBbFeTvcNy75LG/xyILM0l4U+NnwGUypkjR2vJRGVQtw==
decryptedtext: The quick brown fox jumps over the lazy dog
Security warning: the code does not have any exception handling, uses static (hardcoded) keys and is for educational purpose only.
code:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class Main {
public static void main(String[] args) throws GeneralSecurityException, IOException {
System.out.println("RSA 2048 encryption OAEP SHA-1 string");
String dataToEncryptString = "The quick brown fox jumps over the lazy dog";
byte[] dataToEncrypt = dataToEncryptString.getBytes(StandardCharsets.UTF_8);
System.out.println("plaintext: " + dataToEncryptString);
// # # # usually we would load the private and public key from a file or keystore # # #
// # # # here we use hardcoded keys for demonstration - don't do this in real programs # # #
String filenamePrivateKeyPem = "privatekey2048.pem";
String filenamePublicKeyPem = "publickey2048.pem";
// encryption
System.out.println("\n* * * encrypt the plaintext with the RSA public key * * *");
PublicKey publicKeyLoad = getPublicKeyFromString(loadRsaPublicKeyPem());
// use this in production
//PublicKey publicKeyLoad = getPublicKeyFromString(loadRsaKeyPemFile(filenamePublicKeyPem));
String ciphertextBase64 = base64Encoding(rsaEncryptionOaepSha1(publicKeyLoad, dataToEncrypt));
System.out.println("ciphertextBase64: " + ciphertextBase64);
// transport the encrypted data to recipient
// receiving the encrypted data, decryption
System.out.println("\n* * * decrypt the ciphertext with the RSA private key * * *");
String ciphertextReceivedBase64 = ciphertextBase64;
//String ciphertextReceivedBase64 = "l4G3O42LtjI9KkzvcF7SQcpqrkOMJw1sWVuI3FCZ1g+Sp/t05E3ZEXyVV/FnadkKVgmpWBifaYPEdKNBTbuts2F1DfrDz1v14lKlMOcqkJB8OmJUAiKJ1ic414R7M5fECKruqkzOdKlTtdb3MI49Ygrzd/cJxOGEvONo3DAOq1kZvZdmyW+K8m807g2qoy833EHyj9NjVZHuDzXi8fMxbIAI5MrN8ykXZBxkFOAGiITEFbGPxu6gBOPJKsPWJ0SVU53CiI1YwGc76/ov4c7FIA1ZeVsJXKo8CEfYuUc7PKIJ3e5Z7CbiNgr4Z5720Xbi0drUBk/LlYq6m8s/zEIMaQ==";
System.out.println("ciphertextReceivedBase64: " + ciphertextReceivedBase64);
PrivateKey privateKeyLoad = getPrivateKeyFromString(loadRsaPrivateKeyPem());
// use this in production
//PrivateKey privateKeyLoad = getPrivateKeyFromString(loadRsaKeyPemFile(filenamePrivateKeyPem));
byte[] ciphertextReceived = base64Decoding(ciphertextReceivedBase64);
byte[] decryptedtextByte = rsaDecryptionOaepSha1(privateKeyLoad, ciphertextReceived);
System.out.println("decryptedtext: " + new String(decryptedtextByte, StandardCharsets.UTF_8));
}
public static byte[] rsaEncryptionOaepSha1 (PublicKey publicKey, byte[] plaintextByte) throws NoSuchAlgorithmException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException, InvalidAlgorithmParameterException {
byte[] ciphertextByte = null;
Cipher encryptCipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING");
//OAEPParameterSpec oaepParameterSpecJCE = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
// note: SHA1 is the default for Java
OAEPParameterSpec oaepParameterSpecJCE = new OAEPParameterSpec("SHA1", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParameterSpecJCE);
ciphertextByte = encryptCipher.doFinal(plaintextByte);
return ciphertextByte;
}
public static byte[] rsaDecryptionOaepSha1 (PrivateKey privateKey, byte[] ciphertextByte) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
byte[] decryptedtextByte = null;
Cipher decryptCipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING");
//OAEPParameterSpec oaepParameterSpecJCE = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
OAEPParameterSpec oaepParameterSpecJCE = new OAEPParameterSpec("SHA1", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
decryptCipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParameterSpecJCE);
decryptedtextByte = decryptCipher.doFinal(ciphertextByte);
return decryptedtextByte;
}
private static String base64Encoding(byte[] input) {
return Base64.getEncoder().encodeToString(input);
}
private static byte[] base64Decoding(String input) {
return Base64.getDecoder().decode(input);
}
private static String loadRsaPrivateKeyPem() {
// this is a sample key - don't worry !
return "-----BEGIN PRIVATE KEY-----\n" +
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDwSZYlRn86zPi9\n" +
"e1RTZL7QzgE/36zjbeCMyOhf6o/WIKeVxFwVbG2FAY3YJZIxnBH+9j1XS6f+ewjG\n" +
"FlJY4f2IrOpS1kPiO3fmOo5N4nc8JKvjwmKtUM0t63uFFPfs69+7mKJ4w3tk2mSN\n" +
"4gb8J9P9BCXtH6Q78SdOYvdCMspA1X8eERsdLb/jjHs8+gepKqQ6+XwZbSq0vf2B\n" +
"MtaAB7zTX/Dk+ZxDfwIobShPaB0mYmojE2YAQeRq1gYdwwO1dEGk6E5J2toWPpKY\n" +
"/IcSYsGKyFqrsmbw0880r1BwRDer4RFrkzp4zvY+kX3eDanlyMqDLPN+ghXT1lv8\n" +
"snZpbaBDAgMBAAECggEBAIVxmHzjBc11/73bPB2EGaSEg5UhdzZm0wncmZCLB453\n" +
"XBqEjk8nhDsVfdzIIMSEVEowHijYz1c4pMq9osXR26eHwCp47AI73H5zjowadPVl\n" +
"uEAot/xgn1IdMN/boURmSj44qiI/DcwYrTdOi2qGA+jD4PwrUl4nsxiJRZ/x7PjL\n" +
"hMzRbvDxQ4/Q4ThYXwoEGiIBBK/iB3Z5eR7lFa8E5yAaxM2QP9PENBr/OqkGXLWV\n" +
"qA/YTxs3gAvkUjMhlScOi7PMwRX9HsrAeLKbLuC1KJv1p2THUtZbOHqrAF/uwHaj\n" +
"ygUblFaa/BTckTN7PKSVIhp7OihbD04bSRrh+nOilcECgYEA/8atV5DmNxFrxF1P\n" +
"ODDjdJPNb9pzNrDF03TiFBZWS4Q+2JazyLGjZzhg5Vv9RJ7VcIjPAbMy2Cy5BUff\n" +
"EFE+8ryKVWfdpPxpPYOwHCJSw4Bqqdj0Pmp/xw928ebrnUoCzdkUqYYpRWx0T7YV\n" +
"RoA9RiBfQiVHhuJBSDPYJPoP34kCgYEA8H9wLE5L8raUn4NYYRuUVMa+1k4Q1N3X\n" +
"Bixm5cccc/Ja4LVvrnWqmFOmfFgpVd8BcTGaPSsqfA4j/oEQp7tmjZqggVFqiM2m\n" +
"J2YEv18cY/5kiDUVYR7VWSkpqVOkgiX3lK3UkIngnVMGGFnoIBlfBFF9uo02rZpC\n" +
"5o5zebaDImsCgYAE9d5wv0+nq7/STBj4NwKCRUeLrsnjOqRriG3GA/TifAsX+jw8\n" +
"XS2VF+PRLuqHhSkQiKazGr2Wsa9Y6d7qmxjEbmGkbGJBC+AioEYvFX9TaU8oQhvi\n" +
"hgA6ZRNid58EKuZJBbe/3ek4/nR3A0oAVwZZMNGIH972P7cSZmb/uJXMOQKBgQCs\n" +
"FaQAL+4sN/TUxrkAkylqF+QJmEZ26l2nrzHZjMWROYNJcsn8/XkaEhD4vGSnazCu\n" +
"/B0vU6nMppmezF9Mhc112YSrw8QFK5GOc3NGNBoueqMYy1MG8Xcbm1aSMKVv8xba\n" +
"rh+BZQbxy6x61CpCfaT9hAoA6HaNdeoU6y05lBz1DQKBgAbYiIk56QZHeoZKiZxy\n" +
"4eicQS0sVKKRb24ZUd+04cNSTfeIuuXZrYJ48Jbr0fzjIM3EfHvLgh9rAZ+aHe/L\n" +
"84Ig17KiExe+qyYHjut/SC0wODDtzM/jtrpqyYa5JoEpPIaUSgPuTH/WhO3cDsx6\n" +
"3PIW4/CddNs8mCSBOqTnoaxh\n" +
"-----END PRIVATE KEY-----";
}
private static String loadRsaPublicKeyPem() {
// this is a sample key - don't worry !
return "-----BEGIN PUBLIC KEY-----\n" +
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8EmWJUZ/Osz4vXtUU2S+\n" +
"0M4BP9+s423gjMjoX+qP1iCnlcRcFWxthQGN2CWSMZwR/vY9V0un/nsIxhZSWOH9\n" +
"iKzqUtZD4jt35jqOTeJ3PCSr48JirVDNLet7hRT37Ovfu5iieMN7ZNpkjeIG/CfT\n" +
"/QQl7R+kO/EnTmL3QjLKQNV/HhEbHS2/44x7PPoHqSqkOvl8GW0qtL39gTLWgAe8\n" +
"01/w5PmcQ38CKG0oT2gdJmJqIxNmAEHkatYGHcMDtXRBpOhOSdraFj6SmPyHEmLB\n" +
"ishaq7Jm8NPPNK9QcEQ3q+ERa5M6eM72PpF93g2p5cjKgyzzfoIV09Zb/LJ2aW2g\n" +
"QwIDAQAB\n" +
"-----END PUBLIC KEY-----";
}
public static PrivateKey getPrivateKeyFromString(String key) throws GeneralSecurityException {
String privateKeyPEM = key;
privateKeyPEM = privateKeyPEM.replace("-----BEGIN PRIVATE KEY-----", "");
privateKeyPEM = privateKeyPEM.replace("-----END PRIVATE KEY-----", "");
privateKeyPEM = privateKeyPEM.replaceAll("[\\r\\n]+", "");
byte[] encoded = Base64.getDecoder().decode(privateKeyPEM);
KeyFactory kf = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
PrivateKey privKey = (PrivateKey) kf.generatePrivate(keySpec);
return privKey;
}
public static PublicKey getPublicKeyFromString(String key) throws GeneralSecurityException {
String publicKeyPEM = key;
publicKeyPEM = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----", "");
publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
publicKeyPEM = publicKeyPEM.replaceAll("[\\r\\n]+", "");
byte[] encoded = Base64.getDecoder().decode(publicKeyPEM);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pubKey = (PublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
return pubKey;
}
private static String loadRsaKeyPemFile(String filename) throws IOException {
return new String(Files.readAllBytes(Paths.get(filename)), StandardCharsets.UTF_8);
}
}
I'm trying to rebuild a RSA keypair from modulus & private/public exponents. The conversion works correct for the public key but fails to private key when comparing the encoded private keys.
When using this rebuild private/public keypair for encryption it works (!) in Java, but when using the rebuild keypair in PHP, the decryption part fails (encryption is working), so it seems to me that the rebuild private key is something different to the "original" private key.
Just for info: using the "original" keypair everything is working fine in PHP.
So my question: how can I retrieve the "original" private key from (BigInteger) modulus & private exponent?
Edit: see my final edit at the end
My sample code shows the equality of public key vs. rebuild one and that the private keys are different:
Rebuilding of a RSA PrivateKey from modulus & exponent
privateKey equals rebuild: false
publicKey equals rebuild: true
code:
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()));
}
}
Edit: The following programs will show that a RSA private/public keypair derived from encoded keys can get restored and the
encryption and decryption works in Java and PHP. The keys are insecure RSA 512 bit keys and Base64 decoded.
The same keys are then derived from modulus and private/public exponents and the en-/decryption works in Java but not in PHP.
That's why I'd like to get the "original" RSA keys from modulus and exponents, thanks for your kindly help.
Result of Java program:
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
Result of PHP program:
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
Source 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();
}
}
Source 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>';
?>
Final Edit solution & conclusion
If we want to use a RSA private-public keypair for encryption (and signing as well ?) in other systems than Java it is of importance that the private key is saved immeditaly. If we are trying to rebuild the private key from the encoded form (via PKCS8EncodedKeySpec) some data are definitely missing. Those rebuild private keys will fail to work (here in PHP/openssl).
If we need to rebuild a private key from the encoded-form (byte[]) the keys need to get enhanced via a method called "createCrtKey" - this method was written by President James K. Polk and all credits go to him. As links sometimes date out I marked my own answer below as accepted one because the createCrtKey-method is documented there.
Thanks to #President James K. Polk, #Topaco and #michalk for guiding me into the right direction.
The minimal information needed to perform the RSA decrypt operation is the modulus n and the decrypt exponent d. There's an optimization that can be applied to RSA decryption involving the Chinese Remainder Theorem whereby exponentiations are done mod the RSA primes separately and then combined to produce a final value, and thus there are some extra fields fields for this purpose in the RSA Private Key syntax and the Java RSAPrivateCrtKey interface modeled after it.
Now the question being raised here is: When are two RSAPrivateCrtKey instances equal? I would argue that they are equal when they function identically in the RSA algorithm. You are asking for a more narrow definition, namely they are equal when their encoded forms are equal. The problem with this definition is that it is too implementation-specific. Currently, when the "Sun" provider generates a key pair it always orders the primes p and q such that p > q. But I like it the other way, where p < q. The RSAPrivateCrtKey interface does not care either way as it does no checking. The Javadocs for the interface do not specify an order. You can change my code to produce what should be the same encoded form as the current "Sun" implementation simply by reversing the comparison in p.compareTo(q) > 0. However, the default implementation can change to match my preference in the future, and it will if my plan to take over the world succeeds. The Javadocs are the specification, and the implementation may change as long as it complies with the Javadocs.
Below I have provided an implementation of an equality function in which I have tried to incorporate the widest possible notion of equality consistent with the specification. That is, any two RSAPrivateCRTKey instances for which keyEquals returns true should produce identical results when used in the RSA algorithm, and if false is returned then there should be at least one value for which they produce different results.
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));
}
This is the modified version of my program that has the additional code from #President James K. Polk (see link of Topaco above). Even if the rebuild CRT-private key is now longer than the rebuild Private Key it does not match the original (encoded)
private key. As I'm using the encoded private and public keys for a PHP RSA encryption/decryption there is the funny fact that the original keys run successfully but the rebuild ones not...
This version uses a 512 bit keylength that is insecure is for demonstration only (to keep the keys shorter).
result:
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
code:
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();
}
}
I'm trying to re-implement PowerShell's ConvertFrom-SecureString in Java. The documentation there is a bit sparse. All it says is "AES". Trying out various AES encryption examples on the Java side did not get me any further.
Would someone have some pointers for me?
Addendum 1
The pointers provided by the comments look helpful. Thanks!
This PowerShell script shows what I'm trying to "port" to Java, at least the encryption part (with key being just 32 random byte values):
$key = 202,144,73,88,228,1,7,104,95,212,137,87,125,201,80,75,53,18,39,108,60,218,212,151,239,241,34,117,106,184,212,179
$secret = "banana"
Write-Output "secret : $($secret)"
# encrypt
$secretSecured = ConvertTo-SecureString $secret -AsPlainText -Force
$encrypted = ConvertFrom-SecureString -secureString $secretSecured -key $key
Write-Output "encrypted: $($encrypted)"
# decrypt
$secure = $encrypted | ConvertTo-SecureString -key $key
$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure)
$decrypted = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
Write-Output "decrypted: $($decrypted)"
Running it gives this:
secret : banana
encrypted: 76492d1116743f0423413b16050a5345MgB8AEkAdQBpAEIANQBaAHQAeQAvAE0AOQBpAE0ATwByAEEAcgBlAC8AOQB2AGcAPQA9AHwANwBkAGIANwBiADgAYgBhADIAZgA4ADAAZQBlADEAZQA0ADgAYQA1ADUAYgBiADYAZgAyADQAMQA5ADkAMwA5AA==
decrypted: banana
Addendum 2
Here's now a first draft port in Java (also in this Github repository):
import org.junit.Before;
import org.junit.Test;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Base64;
public class EncryptionTest{
SecretKeySpec key;
IvParameterSpec ivSpec;
Cipher cipher;
#Before
public void init() throws Exception{
int[] ints = {202,144,73,88,228,1,7,104,95,212,137,87,125,201,80,75,53,18,39,108,60,218,212,151,239,241,34,117,106,184,212,179};
byte[] bytes = new byte[ints.length];
for (int i=0; i<ints.length; i++){
bytes[i] = (byte)ints[i];
}
key = new SecretKeySpec(bytes, "AES");
byte[] iv = new byte[16];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
ivSpec = new IvParameterSpec(iv);
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
}
#Test
public void testIt() throws Exception{
String secret = "banana";
System.out.println("secret : "+secret);
byte[] encrypted = encrypt(secret);
System.out.println("encrypted: "+Base64.getEncoder().encodeToString(encrypted));
String decrypted = decrypt(encrypted);
System.out.println("decrypted: "+decrypted);
}
byte[] encrypt(String secret) throws Exception{
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
return cipher.doFinal(secret.getBytes());
}
String decrypt(byte[] encrypted) throws Exception{
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
return new String(cipher.doFinal(encrypted));
}
}
Running it (in JUnit) gives this:
secret : banana
encrypted: P51DH0d0FRvUoetaujEyjA==
decrypted: banana
I wonder why the encrypted string here is only 24 characters long, but the PowerShell one has 192 characters?
This question already has answers here:
How to decrypt file in Java encrypted with openssl command using AES?
(4 answers)
AES 256 Encryption Issue
(1 answer)
Closed 7 years ago.
I'm using Java 8 and I'm attempting to emulate the following openssl calls with Java.
Encrypt:
echo -n 'hello world' | openssl enc -a -aes-256-cbc -md sha256 -pass pass:97DE:4F76
U2FsdGVkX18PnO/NLSxJ1pg6OKoLyZApMz7aBRfKhJc=
Decrypt:
echo U2FsdGVkX18PnO/NLSxJ1pg6OKoLyZApMz7aBRfKhJc= | openssl enc -d -a -aes-256-cbc -md sha256 -pass pass:97DE:4F76
hello world
Questions:
My implementation doesn't work. I've visited many other StackOverflow answers but wasn't able to figure out the right implementation. Can anyone point me out in the right direction for solving this?
The openssl system call in the example above uses digest sha256. If I were to use sha1, instead in the Java implemementation, would it be just a matter of changing PBKDF2WithHmacSHA256 with PBKDF2WithHmacSHA1?
Test.java
package test;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class Test {
public static final String PASSWORD = "97DE:4F76";
public static String encryptString(String clearText, String password) {
return "";
}
// echo U2FsdGVkX18PnO/NLSxJ1pg6OKoLyZApMz7aBRfKhJc= | openssl enc -d -a -aes-256-cbc -md sha256 -pass pass:97DE:4F76
//
// see https://stackoverflow.com/a/992413, https://stackoverflow.com/a/15595200,
// https://stackoverflow.com/a/22445878, https://stackoverflow.com/a/11786924
public static String decryptString(String cypherText, String password) {
byte[] dataBase64 = DatatypeConverter.parseBase64Binary(cypherText);
byte[] salt = {
(byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0,
(byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0
};
try {
// generate the key
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); // "PBKDF2WithHmacSHA1"
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
// decrypt the message
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
byte[] decrypted = cipher.doFinal(dataBase64);
String answer = new String(decrypted, "UTF-8");
return answer;
} catch (Exception ex) {
ex.printStackTrace();
}
return "";
}
public static void main(String[] args) {
System.out.println(decryptString("U2FsdGVkX18PnO/NLSxJ1pg6OKoLyZApMz7aBRfKhJc=", PASSWORD));
}
}
this is the current output of running the code above:
java.security.InvalidKeyException: Illegal key size at
javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1039) at
javax.crypto.Cipher.init(Cipher.java:1393) at
javax.crypto.Cipher.init(Cipher.java:1327) at
test.Test.decryptString(Test.java:42) at
test.Test.main(Test.java:55)
Update:
this is the code I ended up implementing after I used this answer: https://stackoverflow.com/a/11786924 -> has the rest of the constants and implementation of EVP_BytesToKey
public static String decryptString(String cypherText, String password) {
try {
// decode the base64 cypherText into salt and encryptedString
byte[] dataBase64 = DatatypeConverter.parseBase64Binary(cypherText);
byte[] salt = Arrays.copyOfRange(dataBase64, SALT_OFFSET, SALT_OFFSET + SALT_SIZE);
byte[] encrypted = Arrays.copyOfRange(dataBase64, CIPHERTEXT_OFFSET, dataBase64.length);
System.out.println("dataBase64 = " + new String(dataBase64));
System.out.println("salt: " + new BigInteger(1, salt).toString(16));
System.out.println("encrypted: " + new BigInteger(1, encrypted).toString(16));
// --- specify cipher and digest for EVP_BytesToKey method ---
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
MessageDigest sha1 = MessageDigest.getInstance("SHA-256");
// create key and IV
final byte[][] keyAndIV = EVP_BytesToKey(
KEY_SIZE_BITS / Byte.SIZE,
cipher.getBlockSize(),
sha1,
salt,
password.getBytes("ASCII"),
ITERATIONS);
SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]);
// initialize the Encryption Mode
cipher.init(Cipher.DECRYPT_MODE, key, iv);
// decrypt the message
byte[] decrypted = cipher.doFinal(encrypted);
String answer = new String(decrypted, "UTF-8"); // should this be "ASCII"?
return answer;
} catch (Exception ex) {
ex.printStackTrace();
}
return "";
}