Reconstructing private and public keys with Bouncy Castle? - java

If I get the actual key with getEncoded from a public or a private key in Bouncy Castle in Java (actual class seems to be BCECPublicKey and BCECPrivateKey). Is it possible to reconstruct the key objects to use them in code?
I found out here in Stack Overflow how to serialize the whole object to binary (and then to disk) and then back to binary and to an object of the appropriate class, but I believe that serialization contains implementation details and if I try to use those keys with anything else than Bouncy Castle, it'll fail. I'm not trying to do that now, but I want to future-proof my program.
This is how I'm creating the keys:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC");
keyPairGenerator.initialize(new ECGenParameterSpec("secp521r1"), new SecureRandom());
java.security.KeyPair keyPair = keyPairGenerator.generateKeyPair();
privateKey = keyPair.getPrivate();
publicKey = keyPair.getPublic();

The KeyFactory is used to convert between encoded keys and the Java classes that represent them. However, the KeyFactory instance doesn't convert directly between a byte array and a Key class. Instead, you must already know what format the encoding uses, and then create a KeySpec object using the byte array in the constructor. The format can be determined by called the getFormat() method on the key. Here is an example illustrating some of these points.
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
public class Main {
public static void main(String[] args) throws Exception{
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC");
keyPairGenerator.initialize(new ECGenParameterSpec("secp521r1"), new SecureRandom());
java.security.KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
System.out.println(privateKey.getFormat());
PublicKey publicKey = keyPair.getPublic();
System.out.println(publicKey.getFormat());
// A KeyFactory is used to convert encoded keys to their actual Java classes
KeyFactory ecKeyFac = KeyFactory.getInstance("EC", "BC");
// Now do a round-trip for a private key,
byte [] encodedPriv = privateKey.getEncoded();
// now take the encoded value and recreate the private key
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(encodedPriv);
PrivateKey privateKey2 = ecKeyFac.generatePrivate(pkcs8EncodedKeySpec);
// And a round trip for the public key as well.
byte [] encodedPub = publicKey.getEncoded();
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(encodedPub);
PublicKey publicKey2 = ecKeyFac.generatePublic(x509EncodedKeySpec);
System.out.println(publicKey2);
}
}

Related

I had generate Public Key by Java spring security and I try to use that public key to encrypt data with Nodejs Crypto but it return error

I had generate public key using Java Spring Security, but I can not use that public key to encrypt the data using Nodejs crypto library. I think it is because of its format(X509).
My Nodejs code
module.exports.encryptRsa = (toEncrypt, pemPath) => {
let absolutePath = path.resolve(pemPath);
let publicKey = fs.readFileSync(absolutePath, "utf8");
let buffer = Buffer.from(toEncrypt);
let encrypted = crypto.publicEncrypt(publicKey, buffer);
return encrypted.toString("base64");
};
My Java code
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(keyAlgorithm);
keyGen.initialize(2048);
KeyPair keyPair = keyGen.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
byte[] privateKeyBytes = privateKey.getEncoded();
byte[] publicKeyBytes = publicKey.getEncoded();
String formatPrivate = privateKey.getFormat(); // PKCS#8
String formatPublic = publicKey.getFormat(); // X.509
FileWriter fos = new FileWriter("publicKey.pem");
fos.write("-----BEGIN RSA PUBLIC KEY-----\n");
fos.write(enc.encodeToString(publicKeyBytes));
fos.write("\n-----END RSA PUBLIC KEY-----\n");
fos.close();
Java's getEncoded() method returns the public key in format called 'spki' by Node crypto. Java's name for that format is "X.509", an unfortunate choice because it causes confusion with certificates of that name.
The proper PEM header for spki keys is simply -----BEGIN PUBLIC KEY-----. Just get rid of RSA in the header and footer.

Store and Retrieve DSA public and private keys in mysql

I have to use DSA (Digital Signature Algorithm) in my project, implementing using Java.I have a problem that I can't store the public and private key of DSA (of type PublicKey & PrivateKey). When I stored it as a blob, can't retrieve to its form (ie., PublicKey). Can anyone help me?
KeyPairGenerator pairgen = KeyPairGenerator.getInstance("DSA");
SecureRandom random = new SecureRandom();
pairgen.initialize(KEYSIZE, random);
KeyPair keyPair = pairgen.generateKeyPair();
PublicKey pu=keyPair.getPublic();
I have to store this up and have to retrieve
Store it as byte[] using pu.getEncoded().
To restore into PublicKey object you load the byte array and make the following call
KeyFactory kf = KeyFactory.getInstance("DSA");
PublicKey pu = kf.generatePublic(new X509EncodedKeySpec(keyBytes));
Optionally you can also encode it with base64.

Android XML RSA, ERROR: java.security.InvalidKeyException: unknown key type passed to RSA

i got problem Encrypting a string using RSA.
my RSA is in XML format, it looks like that:
<RSAKeyValue><Modulus>lT8ykfyV0R8o3mJZZezLKTKJpYB90Pzvp0moLzh9CTGfgsxLKYiAl+YGaoRfQ7hVQos5UlLIONHWKPNco9kKcmL6EBJvFc8wqBnhX0p4ML2WSv1yDIRsm9XXra82WHIa3+fxK8bNUJHrucxmpr9pDRPdZGZkz+Q9s94FcOyFKbs=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>
and i'm trying to encrypt a string using this class:
import java.io.BufferedReader;
import java.io.StringReader;
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.Security;
import javax.crypto.Cipher;
import org.bouncycastle.openssl.PEMReader;
import android.util.Base64;
import android.util.Log;
public class RsaEncryption {
private String publicKey;
public RsaEncryption(String publicKey)
{
this.publicKey = publicKey;
}
/*
* Function to encrypt the data.
*
*/
public String encrypt( String data ) throws Exception
{
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("RSA/None/OAEPWithSHA1AndMGF1Padding", "BC");
byte[] keyBytes = Base64.decode( this.publicKey, 0 );
PublicKey publickey = strToPublicKey(new String(keyBytes));
cipher.init( Cipher.ENCRYPT_MODE , publickey );
// Base 64 encode the encrypted data
byte[] encryptedBytes = Base64.encode( cipher.doFinal(data.getBytes()), 0 );
return new String(encryptedBytes);
}
public static PublicKey strToPublicKey(String s)
{
PublicKey pbKey = null;
try {
BufferedReader br = new BufferedReader( new StringReader(s) );
PEMReader pr = new PEMReader(br);
Object obj = pr.readObject();
if( obj instanceof PublicKey )
{
pbKey = (PublicKey) pr.readObject();
}
else if( obj instanceof KeyPair )
{
KeyPair kp = (KeyPair) pr.readObject();
pbKey = kp.getPublic();
}
pr.close();
}
catch( Exception e )
{
Log.d("CIPHER", e.getMessage() );
}
return pbKey;
}
}
as you can see i'm using bouncycastle's jar
the error that i get is:
java.security.InvalidKeyException: unknown key type passed to RSA
I'm not sure about this part
Cipher cipher = Cipher.getInstance("RSA/None/OAEPWithSHA1AndMGF1Padding", "BC");
maybe this is the problem?
if it is, what need to be there instead?
i did hours of research and still didn't find a solution...
Thanks in advance :)
Cipher cipher = Cipher.getInstance("RSA/None/OAEPWithSHA1AndMGF1Padding", "BC");
maybe this is the problem?
No it's not.
OAEPWith<digest>And<mgf>Padding
Means Optimal Asymmetric Encryption Padding scheme defined in PKCS1, where <digest> should be replaced by the message digest algorithm and <mgf> by the mask generation function. Examples: OAEPWithMD5AndMGF1Padding and OAEPWithSHA-512AndMGF1Padding.
Reference Standard Names and RFC 4055.
The problem is in your Public Key generation. As your key is in XML, and Base64 encoded:
First you need to separate modulus and exponent.
Then Base64 decode both modulus and exponent.
After decoding you will get the byte array of modulus and exponent, so you can easily prepare public key object like the following procedure:
BigInteger modBigInteger = new BigInteger(1, modulus);//modulus must be byte array
BigInteger exBigInteger = new BigInteger(1, exponent);//exp must be byte array
RSAPublicKeySpec spec = new RSAPublicKeySpec(modBigInteger, exBigInteger);
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey publicKey = factory.generatePublic(spec);
XML is not PEM.
You need to extract the modulus and the public exponent from the XML and then generate a key using an "RSA" KeyFactory instance and a RSAPublicKeySpec.

How to programmatically generate a self signed certificate?

I have a RSA public key (2048 bit) generated by a HSM, that key has been saved in a file (with a size of 256 byte) and is encoded as DER.
Is it possibile to programmatically create a self-signed certificate using JDK API (without BouncyCastle) starting from that file?
I'm stuck with the first step, because I'm trying to load the key file to create a PublicKey object:
import java.io.FileInputStream;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import org.apache.commons.io.IOUtils;
public class Crypto {
public static void main(String[] args) throws Exception {
byte[] byteArray = IOUtils.toByteArray(new FileInputStream("/tmp/pub.key"));
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(byteArray);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pub = kf.generatePublic(spec);
....
}
}
but I get this exception:
Exception in thread "main" java.security.spec.InvalidKeySpecException: Only RSAPublicKeySpec and X509EncodedKeySpec supported for RSA public keys
at sun.security.rsa.RSAKeyFactory.generatePublic(RSAKeyFactory.java:289)
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:184)
at java.security.KeyFactory.generatePublic(KeyFactory.java:304)
at org.alex.Crypto.main(Crypto.java:17)
Is there a way to do that?
Use X509EncodedKeySpec (which internally actually used PKCS#1 encoding for RSA keys) instead. Keep the rest of the code identical. PKCS#8 is for private keys, not public keys (because it uses the PKCS#8 internal structure required to wrap a key with another key, and wrapping public keys makes no sense).
The exception is telling you the issue! => Only RSAPublicKeySpec and X509EncodedKeySpec supported for RSA public keys
You are trying to use PKCS8EncodedKeySpec, which is not supported, create RSAPublicKeySpec or X509EncodedKeySpec class
Example
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
byte[] input = new byte[] { (byte) 0xbe, (byte) 0xef };
Cipher cipher = Cipher.getInstance("RSA/None/NoPadding", "BC");
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(
"12345678", 16), new BigInteger("11", 16));
RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(new BigInteger(
"12345678", 16), new BigInteger("12345678",
16));
RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(pubKeySpec);
RSAPrivateKey privKey = (RSAPrivateKey) keyFactory.generatePrivate(privKeySpec);
cipher.init(Cipher.ENCRYPT_MODE, pubKey);

How to convert Byte array to PrivateKey or PublicKey type?

I am using RSA algorithm to generate public and private key
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
keyGen.initialize(1024);
final KeyPair key = keyGen.generateKeyPair();
final PrivateKey privateKey=key.getPrivate();
final PublicKey publickey=key.getPublic();
after that these keys are encoded using Base64 encoder and save it into database.
How to convert this encoded String to Private and Public Key Type in java is to decrypt file.
when decoding this String using Base64Decoder will get a byte array. how to convert this Byte array to public or private key type?
If you have a byte[] representing the output of getEncoded() on a key, you can use KeyFactory to turn that back into a PublicKey object or a PrivateKey object.
byte[] privateKeyBytes;
byte[] publicKeyBytes;
KeyFactory kf = KeyFactory.getInstance("RSA"); // or "EC" or whatever
PrivateKey privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
PublicKey publicKey = kf.generatePublic(new X509EncodedKeySpec(publicKeyBytes));

Categories