Failed to RSA decrypt with java - java

My password is encrypted with RSA in an android app. On the server side, I need to decrypt it., What I have is a .pem file, and the php code for decrypting:
function privatekey_decodeing($crypttext, $fileName, $fromjs = FALSE)
{
$key_content = file_get_contents( $fileName );
$prikeyid = openssl_get_privatekey( $key_content, "1234" );
$crypttext = str_replace(' ', '+', $crypttext);
$crypttext = base64_decode( $crypttext );
$padding = $fromjs ? OPENSSL_NO_PADDING : OPENSSL_PKCS1_PADDING;
if( openssl_private_decrypt( $crypttext, $sourcestr, $prikeyid, $padding ) )
{
return $fromjs ? rtrim( strrev( $sourcestr ), "/0" ) : "" . $sourcestr;
}
return;
}
the fileName is frivatekey file(.pem file)
Now I need to use java to decrypt it. I have tried some methods, all have failed. Here is what I have tried:
using the .pem file to generate a .der key file
reading the .der file to get the privateKey
using the byte[] read from .der file to generate keyfile
public static PrivateKey generatePrivateKey(byte[] key)
throws NoSuchAlgorithmException, InvalidKeySpecException {
KeySpec keySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
}
decrypt my password
public static byte[] decrypt(PrivateKey privateKey, byte[] data)
throws Exception {
Cipher ci = Cipher.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
ci.init(Cipher.DECRYPT_MODE, privateKey);
return ci.doFinal(data);
}
But it does not work, and I do not know where is going wrong.
In the php code I see $prikeyid = openssl_get_privatekey( $key_content, "1234" );
But I don't know what does the "1234" means. Does it mean using "1234" to encrypt the keyfile? Is this the reason the decrypt failed?

$padding = $fromjs ? OPENSSL_NO_PADDING : OPENSSL_PKCS1_PADDING;
These are both bad options:
Unpadded RSA is insecure
PKCS1 padding (which is also the default padding mode) is vulnerable to chosen-ciphertext attacks; thus it's also insecure
Please don't implement RSA yourself. You're going to make your application incredibly insecure.
Recommended reading:
Cryptographic right answers
The original cryptographic right answers
Recommended PHP cryptography libraries
Crypto Fails
How to safely implement cryptography in any application (covers Java and PHP)

Related

Unit-Test in Java: Encryption and Decryption given an iOS KeyPair in ASN.1-Format

Objective
Writing an Encryption and Decryption-Test in a Java17 SpringBoot Service using RSA-keys generated on an iOS mobile device.
Hence:
Target: RSA-4096 is used throughout
This is about an iOS mobile device (Swift) <-> SpringBoot Service (Java17) context
Why is this question important?
The aim of the process:
On the iOS mobile divice a KeyPair is created and the public key (only) is sent to the SpringBoot Service.
Somewhen payload is encrypted in the SpringBoot Service and held for collection.
Later on the iOS mobile device pulls the encrypted data and decrypts it locally with its private key to retreive the payload.
Sidenote
This had to be done with an Android mobile device as well and it works fine.
Android serves PKCS-Formatted Keys.
Luckily regarding Android, both sides may use the standard javax.crypto-Library (only).
Hurdle along the way
Key format: ASN.1
The iOS-device does not export keys along the plain PKCS-Format.
It uses ASN.1-Format from the same 2003 RFC:
https://www.rfc-editor.org/rfc/rfc3447#appendix-A
Nevertheless, the keys can be extracted and exported from the keyChain (not the Secure Enclave).
SampleCode to extract the Keys from the KeyPair (Swift):
// Keypair attributes
let tag = "my_personal_keystore_keypair_alias".data(using: .utf8)!
let attributes: [String: Any] =
[kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits as String: "4096",
kSecPrivateKeyAttrs as String:
[kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: tag]
]
var error: Unmanaged<CFError>?
// Generate PrivateKey / Keypair
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
// Extract PrivateKey
guard
let privateKeyOptional = SecKeyCopyExternalRepresentation(privateKey, nil) else {
return "Key generation did not succeed."
}
let privateKeyData = privateKeyOptional as Data
let base64PrivateKey = privateKeyData.base64EncodedString()
print("PrivateKey, base64: ", base64PrivateKey)
// Extract PublicKey
guard let publicKey = SecKeyCopyPublicKey(privateKey),
let publicKeyOptional = SecKeyCopyExternalRepresentation(publicKey, nil) else {
return "PublicKey could not be accessed."
}
let publicKeyData = publicKeyOptional as Data
let base64PublicKey = publicKeyData.base64EncodedString()
print("PublicKey, base64: ", base64PublicKey)
Sample Base64-encoded Keys (Java):
// === Sample KeyPair START ===
String publicKey = "MIICCgKCAgEAu2QLxnFmEMeZTtiX4DRUmeqsocOFtqyP2cKAiWCjbr75D+Ymzem0T3/oJov4G68yPMMAB/scdrxlpu3D7UmOUjeQ4aNUW3u/ZnHoDRdph7Jc/2ed6jAmPV/867BV78XZXL0O9Ofru6W+jCwuFunJN3IWhiVJhwCmYgn3wQDl6TeIYKysZ2XKWauFFlLYY9RSZAoCCyEr5PPS9irJhhiQiP2bv2cqh0eW/8N1DXBDSx80bsgtrhAFaG9UU/P5ApoCr+EmwqM6wEjYbTV/fWi0b74UGxiE2UtvEMHfp+XJ+JkbXH4GT7v+Gwhtkuq0+3wrSSD3bCZ5by7ti1+MK6bugsQ96+teY3Bd3jKHaGVLvYN2mIGwRW7Jmbf93B9s8z5nmvNJibF67Ru+0M86VJBIjmhbmoEJwPda6FDTqJObRmQwqGj6JSIwTeSnEqKA/8PZXn+qID0HaDly4uoVb5q2yDaykvqmA8k5IGV5UOjQvqf6saD4UL8nYmubUcQxLm5m9AC9J4sAmJwWGs5tuYr7auyH+GFIOmn7irtC+YX/lNAypgvnklJEWNmz/M/nBq1nDD4eygpY0E/k/cWMloLTwwvRbjj0ApUbGz1RUB2UP9ZI4Avb17UKhXXylLra9tPz1QtNIhwhIOX3tQzIQMiyK7aMJJUryBJajMzHowYSYQMCAwEAAQ==";
String privateKey = "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";
// === Sample KeyPair END ===
Solving the hurdle
Afaik the javax.crypto-Library does not support ASN.1-Formatted RSA-Keys.
Therefore the bouncycastle-dependency was used to parse the PublicKey (SpringBoot pom.xml):
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
Converting the PublicKey-String into a PublicKey-Class (Java):
public Optional<PublicKey> createIOSPublicKeyFromString(String publicKeyASN1) throws NoSuchAlgorithmException, InvalidKeySpecException {
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyASN1);
ASN1Sequence sequence = ASN1Sequence.getInstance(publicKeyBytes);
ASN1Integer modulus = ASN1Integer.getInstance(sequence.getObjectAt(0));
ASN1Integer exponent = ASN1Integer.getInstance(sequence.getObjectAt(1));
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus.getPositiveValue(),
exponent.getPositiveValue());
KeyFactory factory = KeyFactory.getInstance("RSA");
return Optional.ofNullable(factory.generatePublic(keySpec));
}
The encryption using the PublicKey works without further interruption (Java):
public String encryptPayload(String payload, PublicKey publicKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
Cipher encryptCipher = Cipher.getInstance(encryptionProperties.getAlgorithm());
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] secretMessageBytes = payload.getBytes(StandardCharsets.UTF_8);
byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);
return Base64.getEncoder().encodeToString(encryptedMessageBytes);
}
The Problem
Please note - this is still in a testing scenario.
The aim is to decrypt within a unit-test of the SpringBoot Service.
The PrivateKey
Although the PublicKey could be parsed the described way, the parsing of the PrivateKey is refused.
java.security.InvalidKeyException: RSA keys must be at least 512 bits long
As the keys had been initialized using a KeySize of 4096, the error message is irritating.
Nevertheless, I cannot find the mistake so far.
Solution space
Multiple approaches may be suitable to solve this issue:
Execute the PrivateKey creation using the bouncycastle dependency.
Conduct the PrivateKey creation using the javax-crypto dependency.
Exporting the PublicKey generated in iOS (Swift) in a PKCS-Format
Solution (3) would erase the need of the bouncycastel-dependency and makes the need of THIS additional test obsolete :)
All approaches are fine ... .. . ;-)
Finally
Thanks for reading & sharing - any help is appreciated :)

AES Encryption in android and decryption in nodejs

I was trying encryption in android and decryption in nodejs server. I generated an AES 128bit key and encrypt it using AES algorithm and then encrypt this generated key using RSA algorithm. Then send both to the server. But while decrypting on the server side, I think the RSA decryption works fine but have a problem in AES decryption.
I'm not getting the string in server side that I encrypted on the client side.
This is the code for the encryption on android side:
String encryptedSecretKey;
String cipherTextString;
// 1. generate secret key using AES
KeyGenerator keyGenerator = null;
keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
// 2. get string which needs to be encrypted
String text = "This is the message to be encrypted";
// 3. encrypt string using secret key
byte[] raw = secretKey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
cipherTextString = Base64.encodeToString(cipher.doFinal(text.getBytes(Charset.forName("UTF-8"))), Base64.DEFAULT);
// 4. get public key
X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(Base64.decode(publicKeyString, Base64.DEFAULT));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(publicSpec);
// 5. encrypt secret key using public key
Cipher cipher2 = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
cipher2.init(Cipher.ENCRYPT_MODE, publicKey);
encryptedSecretKey = Base64.encodeToString(cipher2.doFinal(secretKey.getEncoded()), Base64.DEFAULT);
Then send this to the server side.
The code for server side is given below:
var encryptedMessage = req.body.cipherText;
var encryptedAesKey = req.body.secretKey;
//printing those values
console.log("\nEncryptedMessage: \n" + encryptedMessage);
console.log("\nEncrypted key: \n" + encryptedAesKey);
var privateKey = fs.readFileSync('././Keys/privkey_server.pem', "utf8");
var bufferForAesKey = new Buffer(encryptedAesKey, "base64");
var obj = {
key: privateKey
// , padding: constants.RSA_PKCS1_PADDING
// , padding: constants.RSA/ECB/OAEPWithSHA-1
};
var decryptedAes = crypto.privateDecrypt(obj, bufferForAesKey);
console.log("Decrypted AES: " + decryptedAes);
var decryptedAesKeyString = decryptedAes.toString("base64");
console.log("Decrypted AES Key: " + decryptedAesKeyString);
var bufferForAES = new Buffer(decryptedAes, "base64");
//decrypting using AES
var bufferForEncryptedMsg = new Buffer(encryptedMessage, "base64");
var decipher = crypto.createDecipher('aes-128-cbc',bufferForAES);
decipher.setAutoPadding(false);
var dec = decipher.update(bufferForEncryptedMsg,"base64", "utf8");
dec += decipher.final("utf8");
console.log(dec);
Here the final result 'dec' is not giving the correct result but the intermediate results are same in client and server. That means, RSA works fine but have problem in AES.
The output is given below:
EncryptedMessage:
SfosHg+cTrQXYUdF0FuqCJMHgfcP13ckp2L0B9QqOcl8UtWnnl8fLi5lxgR2SKOj
Encrypted key:
C/pa52PZda3xShrPXkHZx8OL6sW4JBEhG/ggNAoHhSVXIGt+iDq/B1ByG5yStBGF3GFJRQT0aGsG
+bZJydP7j9gTivmt99H/bxiZan4CHZnqfGKG1dJCI7ILAYZMCw7JhIcRC8qHMM4LMdF+rxRhENDe
alUfnsLWpcrX9J6fKejJ7EWnWQ1VadKqCDmrJ5xw0lBbsOpwN/vY09+VhF4WkOz8Y3cQGk+FVdz5
tr4L9/jgXlTZdOC2KVBLSH+9pvtHwMWFKKoDSAzvkil4htBjbWTqlBuEINC4I/J/4P3RX2riT5Pv
xHQi/Dv7vdBlo9AEdvWe3Ek8oBleIpmIQHXwQWknPOYghhBAVmACG/tbEQcAtbcmRLruT/XzjPJt
HNBt2HeG9JHYKNoHC3kOuJdnlGe8mv8k0Nzwj04RhEGKSmPIiu/oDgYwS0l96KIlS2ELqBlS5O0L
AJ+RBG7m0WwC9dfrufsuwu0+SPUmg5/ElXRmA3T81lXtQqQbGg8G6r/bAVFGduy4a49s/VWoylx+
/sI079IwyY0IOfwQTVGZRyDC5O1ZBjoYv2+TRo3bjG8GXNQoybkmWkhgotcqVD9mXO67D2NBsFPT
EJnw+1ApSqR7ggIAF+qsMxejFKBICBL/4J8FP4+obA07J1pWiciTRKX+G130qzIBKM08Zdaf/50=
Decrypted AES: %Kp[ϪS�/�W l��9ӊ˽��~��
B�A�
Decrypted AES Key: JUtwW8+qU6Mv/FcgbMbkOdOKy72pun4B490KQrRB4QQ=
T�Ϝ��u��q�
���w�p���u`�̗r[`H0[tW��=��~i-�W
Here the Decrypted AES key is same as the key that we generate in android. But the final output is not giving the desired result. Is there any error in my code??
Neardupe Decrypting strings from node.js in Java? which is the same thing in the opposite direction.
[In Java] I generated an AES 128bit key and encrypt [with] it using AES algorithm and then encrypt this generated key using RSA algorithm.
No you didn't. Your Java code instantiates a KeyGenerator for AES-128, but doesn't use it to generate any key. The key you actually used (and as you say the server correctly decrypted from RSA-OAEP) is 32 bytes, corresponding to AES-256.
But your main problem is that createDecipher takes a password NOT the key. Per the doc
crypto.createDecipher(algorithm, password[, options])
The implementation of crypto.createDecipher() derives keys using the OpenSSL function EVP_BytesToKey with the digest algorithm set to MD5, one iteration, and no salt.
You passed what is actually a key as a password; this results in nodejs using a key that is completely different from the one used in Java and thus getting completely wrong results. You should instead use createDecipheriv which does take the key, and IV (Initialization Vector).
And that is your other problem. To decrypt you must use the same IV as encrypt did, normally by including the IV with the ciphertext sent from the sender to receiver, but you don't. As a result the following (simplified) code cannot decrypt the first 16 bytes of your data, but does the rest.
const crypto = require('crypto');
msg = Buffer.from('SfosHg+cTrQXYUdF0FuqCJMHgfcP13ckp2L0B9QqOcl8UtWnnl8fLi5lxgR2SKOj','base64');
aeskey = Buffer.from('JUtwW8+qU6Mv/FcgbMbkOdOKy72pun4B490KQrRB4QQ=','base64');
dec = crypto.createDecipheriv('aes-256-cbc',aeskey,Buffer.alloc(16)/*this should be the IV*/);
console.log(dec.update(msg,'','latin1')+dec.final('latin1'));
// I used latin1 instead of utf8 because the garbaged first block
// isn't valid UTF-8, and the rest is ASCII which works as either.
->
Y;øï«*M2WÚâeage to be encrypted
// some garbaged chars are control chars and Stack (or browser?)
// may not display them but there really are 16 in total
As an aside, the statement in the doc that 'Initialization vectors [must] be unpredictable and unique ... [but not secret]' is correct for CBC mode, but not some other modes supported by OpenSSL (thus nodejs) and Java. However, that's not a programming Q and thus offtopic here; it belongs on crypto.SX or possibly security.SX where it has already been answered many times.

RSA encrypt using JSEncrypt and decrypt using BouncyCastle (Java)

This might be a duplicate of this answered question, but I can't seem to get the same results. Hoping for some guidance here.
JSEncrypt (client)
let encrypt = new Encrypt.JSEncrypt();
encrypt.setPublicKey(this.publicKey); // retrieved from server
encrypt.encrypt(password);
BouncyCastle (server) - RSA key generation
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(1024);
KeyPair pair = generator.generateKeyPair();
PublicKey pubKey = pair.getPublic();
PrivateKey privKey = pair.getPrivate();
// returned to client
String publicKeyStr = new String(Base64.encodeBase64(pubKey.getEncoded()));
String privateKeyStr = new String(Base64.encodeBase64(privKey.getEncoded()));
BouncyCastle (server) - Decryption
Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// org.apache.commons.codec.binary.Hex
byte[] cipherText = cipher.doFinal(Hex.decodeHex(encrypted.toCharArray()));
decrypted = new String(cipherText, BaseConstant.ENC_UTF8);
Error
org.apache.commons.codec.DecoderException: Illegal hexadecimal character I at index 0
at org.apache.commons.codec.binary.Hex.toDigit(Hex.java:178)
at org.apache.commons.codec.binary.Hex.decodeHex(Hex.java:89)
One thing I noticed is the length of encrypted text by JSEncrypt, which is 172, while encryption at server side produces 256.
The answered question mentioned to use RSA/None/PKCS1Padding, which I had already set. What else could I be missing?
The error occurs in Hex.decodeHex() method, which means that your data is not a Hex encoded string.
JSEncrypt.encrypt() method returns the encrypted data in Base64 (instead of Hex string). In order to decrypt it, you must decode it from base64 format.
So instead of:
byte[] cipherText = cipher.doFinal(Hex.decodeHex(encrypted.toCharArray()));
Do this:
byte[] cipherText = cipher.doFinal(Base64.decodeBase64(encrypted.toCharArray()));
You can also solve this problem just from the client side. See the code below:
let encrypt = new Encrypt.JSEncrypt();
encrypt.setPublicKey(this.publicKey);
encrypt.getKey().encrypt(password);
Just add getKey() after encrypt. It worked for me! I encrypted my password into Hex string using this approach.

C++/Openssl Get RSA key from encoded bytes (encoded by java)

Does somebody know how I can create an RSA key in C++ from an encoded byte array?
My problem is that I try to develop a C++ client that is interacting with a server which is coded in Java.
Well in Java the client receives the rsa key encoded as an byte array, decodes it to a RSA RSAPublicKey and encrypts a message with this key.
The java server/client code:
public static PublicKey decodePublicKey(byte[] p_75896_0_)
{
try
{
X509EncodedKeySpec var1 = new X509EncodedKeySpec(p_75896_0_);
KeyFactory var2 = KeyFactory.getInstance("RSA");
return var2.generatePublic(var1);
}
catch (NoSuchAlgorithmException var3)
{
;
}
catch (InvalidKeySpecException var4)
{
;
}
field_180198_a.error("Public key reconstitute failed!");
return null;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
this.publicKey = CryptManager.decodePublicKey(data.readByteArray());
After that the client is doing some encrypting stuff with his key.
The key gets sent like this:
public static final KeyPair keys;
static
{
try
{
KeyPairGenerator generator = KeyPairGenerator.getInstance( "RSA" );
generator.initialize( 1024 );
keys = generator.generateKeyPair();
} catch ( NoSuchAlgorithmException ex )
{
throw new ExceptionInInitializerError( ex );
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
byte[] pubKey = keys.getPublic().getEncoded();
writeBytes(pubKey);
My problem is how to get the key from the byte array in C++.
Update:
Im currently working on this code:
char* publicKey = ...
int publicKeyLength = 162;
EVP_PKEY* key = EVP_PKEY_new();
if(d2i_PUBKEY(&key, (const unsigned char**) &publicKey, publicKeyLength) != 0){
logError("Problem!");
}
logMessage("Key: "+to_string((uint64_t) (void*) key));
Well my problem now is that i have an SIGSEGV error on the third line and dont know what this course. Well the key should be valid.
What Java returns for the public key is a SubjectPublicKeyInfo structure, which doesn't just contain the (PKCS#1 encoded) values for the public key, but also the key identifier etc.
So to decode this you have to type "decode SubjectPublicKeyInfo openssl" in your favorite search engine. Then you'll find (after some scrolling) the following information from here:
d2i_PUBKEY() and i2d_PUBKEY() decode and encode an EVP_PKEY structure
using SubjectPublicKeyInfo format. They otherwise follow the conventions
of other ASN.1 functions such as d2i_X509().
Obviously you'd need the decoding algorithm.
Note that openssl is C so beware of buffer overruns when decoding stuff. I'd rather have a 1024 bit RSA key that is used with secure software than a 2048 bit key with software full of buffer overruns.
Needless to say you need to trust the public key before importing it. There is a reason why it is called the public key infrastructure (PKI).

RSA public and private key as String variables in Java

For obvious security reasons i need to encrypt and decrypt User's PIN codes with RSA private and public key, I have found working solution, which looks like:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair rsaKeyPair = kpg.genKeyPair();
byte[] txt = "This is a secret message.".getBytes();
System.out.println("Original clear message: " + new String(txt));
// encrypt
Cipher cipher;
try {
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, rsaKeyPair.getPublic());
txt = cipher.doFinal(txt);
} catch (Throwable e) {
e.printStackTrace();
return;
}
System.out.println("Encrypted message: " + new String(txt));
// decrypt
try {
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, rsaKeyPair.getPrivate());
txt = cipher.doFinal(txt);
} catch (Throwable e) {
e.printStackTrace();
return;
}
System.out.println("Decrypted message: " + new String(txt));
}
everything works fine, but in this example key-pair is not static and generate new values everytime, but I need to use same keys, which are represented as String variables:
public static final String PrivateKey = "MIICXAIBAAKBgQDx0PSJr6zEP9914k1eM+sS8/eW+FenhBQI/jf6ARe8kZHFig9Y"
+ bla bla bla
+ "wdK3jBzObK319yNFr/2LukNZ9Bgv7fS78roBvxbe2gI=";
public static final String PublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDx0PSJr6zEP9914k1eM+sS8/eW"
+ bla bla bla
+ "jYo5w2Nhxe2cukCQMQIDAQAB";
Is there any way to cast these variables to PublicKey and PrivateKey Class?
If I understand what you want, to obtain PublicKey and PrivateKey instances from your static variables you can do, for example, this way:
private static final String privateKeyString = "...";
private static PrivateKey privateKey;
private static final String publicKeyString = "...";
private static PublicKey publicKey;
static {
KeyFactory kf;
try {
kf = KeyFactory.getInstance("RSA");
byte[] encodedPv = Base64.decodeBase64(privateKeyString);
PKCS8EncodedKeySpec keySpecPv = new PKCS8EncodedKeySpec(encodedPv);
privateKey = kf.generatePrivate(keySpecPv);
byte[] encodedPb = Base64.decodeBase64(publicKeyString);
X509EncodedKeySpec keySpecPb = new X509EncodedKeySpec(encodedPb);
publicKey = kf.generatePublic(keySpecPb);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
}
}
After (mostly) concurring with #JB that passwords (usually) shouldn't be encrypted, they should be "hashed" -- using a method specifically designed to "stretch" and salt such as scrypt, not a fast hash like SHA-1 -- and also noting that RSA-512 as used in your original code is broken and even RSA-1024 as apparently used in your modification is considered weak:
Your PrivateKey value appears (from its beginning) to be base64 of a plain PKCS#1 DER encoding, which basically is used only by OpenSSL and things that use OpenSSL (format) like older versions of OpenSSH. The Java standard "Sun" providers do not handle this, although I think BouncyCastle might if you want to explore that. For Sun you need to convert it to binary DER from base64; wrap it into PKCS#8 format (which in binary is just adding a header and maybe EOC trailers because the algorithm-specific part of PKCS#8 for RSA is PKCS#1); and put it in a PKCS8EncodedKeySpec and run it through generatePrivate of a KeyFactory of type RSA. See
http://docs.oracle.com/javase/8/docs/api/java/util/Base64.html (Java8 only)
http://docs.oracle.com/javase/8/docs/api/java/security/KeyFactory.html
https://www.rfc-editor.org/rfc/rfc5208#section-5 for the structure of unencrypted PKCS#8 (Java doesn't do the encrypted format in section 6) and look at the publickey form for the OID for RSA.
Alternatively add the header/trailer to make it proper PEM, use OpenSSL to convert it to PKCS#8 (unencrypted), and optionally binary at the same time, and run that through generatePrivate.
Your PublicKey similarly appears to be base64 of an X.509 SubjectPublicKeyInfo encoding, which OpenSSL (but not OpenSSH) uses and standard Java does support under the name "X.509". So just convert from base64 to binary, put in an X509EncodedKeySpec, and run through generatePublic of the RSA KeyFactory. Note if your encryption will be done remote or distributed, which is the usual scenario for publickey-encryption, the encryptor must be certain to use the correct publickey; if an attacker can substitute a wrong publickey they can decrypt and steal at least some of your supposedly secure data. That's why real PK systems don't use a plain publickey, they use a certificate, either X.509 like SSL/TLS and S/MIME, or web-of-trust like PGP.
I got this running doing the following:
public Key loadPrivateKey(String stored) throws GeneralSecurityException {
PKCS8EncodedKeySpec keySpec =
new PKCS8EncodedKeySpec(
Base64.getDecoder().decode(stored.getBytes(StandardCharsets.UTF_8)));
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(keySpec);
}
public Key loadPublicKey(String stored) throws GeneralSecurityException {
byte[] data = Base64.getDecoder().decode(stored.getBytes(StandardCharsets.UTF_8));
X509EncodedKeySpec spec = new X509EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance("RSA");
return fact.generatePublic(spec);
}
You must also to remove -----BEGIN PRIVATE KEY-----, -----END PRIVATE KEY----- and all \n from the strings that contain you keys.

Categories

Resources