I have a publicKey/privateKey pair generated from this function:
public static void generateKey() {
try {
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
keyGen.initialize(2048);
final KeyPair key = keyGen.generateKeyPair();
File privateKeyFile = new File(PRIVATE_KEY_FILE);
File publicKeyFile = new File(PUBLIC_KEY_FILE);
// Create files to store public and private key
if (privateKeyFile.getParentFile() != null) {
privateKeyFile.getParentFile().mkdirs();
}
privateKeyFile.createNewFile();
if (publicKeyFile.getParentFile() != null) {
publicKeyFile.getParentFile().mkdirs();
}
publicKeyFile.createNewFile();
// Saving the Public key in a file
ObjectOutputStream publicKeyOS = new ObjectOutputStream(
new FileOutputStream(publicKeyFile));
publicKeyOS.writeObject(key.getPublic());
publicKeyOS.close();
// Saving the Private key in a file
ObjectOutputStream privateKeyOS = new ObjectOutputStream(
new FileOutputStream(privateKeyFile));
privateKeyOS.writeObject(key.getPrivate());
privateKeyOS.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Now I want to convert publicKey to base64 while writing and use that base64 decode to get publicKey back ,how can that be done?
Generally if you want to store a file in base 64 you can simply encode the byte array. You can even put a Base64 stream in between the ObjectOutputStream and FileOutputStream (helpfully provided by the Base64 class within Java 8).
However, public keys and private keys have default encodings which can be accessed using their getEncoded methods:
PublicKey publicKey = key.getPublic();
byte[] encodedPublicKey = publicKey.getEncoded();
String b64PublicKey = Base64.getEncoder().encodeToString(encodedPublicKey);
try (OutputStreamWriter publicKeyWriter =
new OutputStreamWriter(
new FileOutputStream(publicKeyFile),
StandardCharsets.US_ASCII.newEncoder())) {
publicKeyWriter.write(b64PublicKey);
}
This saves the public key in SubjectPublicKeyInfo format, something that can be read and written by multiple types of software and cryptographic libraries.
For instance, you can paste it in an online ASN.1 decoder (the online decoder will itself convert it to hex, but it will parse base 64 as well). The format of bytes are in so called ASN.1 / DER (which is a generic format, just like you can encode multiple types of files in XML).
If you want to have the key in OpenSSL compatible format (with a "PUBLIC KEY" header and footer) you can use a library such as Bouncy Castle (e.g. org.bouncycastle.openssl.jcajce.JcaPEMWriter).
Related
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).
I am encrypting using Bouncy Castle. I am using RSA and public key is stored in PEM file. I am finding that when I run the code in simple console project (not Android project) everything works fine - meaning the encrypted string can be decrypted using the private key. However, when I run the same code in Android app, the encrypted byte array is different for the same public key and is not recognized as valid encryption for the given key pair.
Details:
Here is the code that encrypts string using Bouncy Castle and taken out of the console project. This one works fine and produces the encrypted string which is valid encryption for the key pair and can be decrypted.
private static void encrypt() {
try {
//This example uses the Bouncy Castle library
Security.addProvider(new BouncyCastleProvider());
String plainText = "This needs to be encrypted";
String public_key_file = "PublicKey.pem";
//Load public key
PEMParser parser = new PEMParser(new FileReader(public_key_file));
Object key = parser.readObject();
parser.close();
PublicKey pubKey = null;
if (key instanceof SubjectPublicKeyInfo) {
SubjectPublicKeyInfo spki = (SubjectPublicKeyInfo) key;
pubKey = KeyFactory.getInstance("RSA").generatePublic
(new X509EncodedKeySpec(spki.getEncoded()));
}
//Encrypt the plain text
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encrypted_data = cipher.doFinal(plainText.getBytes());
String encoded_data = new String(Base64.encode(encrypted_data));
System.out.println("Encrypted Value:");
System.out.println(encoded_data);
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
Now here is the code taken out of the Android app. The only important difference is that the key file is read from Assets folder. This code also produces encrypted string but it is not valid for the key pair and can't be decrypted.
public void encrypt(Context context) {
try {
//This example uses the Bouncy Castle library
Security.addProvider(new BouncyCastleProvider());
String plainText = "This needs to be encrypted";
String public_key_file = "PublicKey.pem";
//Load public key
InputStream inputStream = context.getAssets().open(public_key_file);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
// read from the input stream reader
PEMParser parser = new PEMParser(inputStreamReader);
Object key = parser.readObject();
parser.close();
PublicKey pubKey = null;
if (key instanceof SubjectPublicKeyInfo) {
SubjectPublicKeyInfo spki = (SubjectPublicKeyInfo) key;
pubKey = KeyFactory.getInstance("RSA").generatePublic
(new X509EncodedKeySpec(spki.getEncoded()));
}
//Encrypt the plain text
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encrypted_data = cipher.doFinal(plainText.getBytes());
String encoded_data =
new String(Base64.encode(encrypted_data));
Log.d("MyApp", "Encrypted Value:");
Log.d("MyApp", encoded_data);
} catch (Exception ex) {
Log.d("MyApp", ex.getMessage());
}
}
In both the cases, following things are same:
Public Key
Actual code written (with difference of reading from Assets folder and logging to Logcat)
JDK version (1.7)
Bouncy Castle libraries (bcpkix-jdk15on-152.jar and bcprov-jdk15on-152.jar)
What's different: The environment. One is console program and the other is Android App.
On further investigation while debugging
I observed that when running the console program the KeyFactory returns instance of "sun.security.rsa.RSAPublicKeyImpl". However when running android app, the KeyFactory returns instance of "com.android.org.conscrypt.OpenSSLRSAPublicKey". Not sure if that's the problem. The encrypted byte array is different for same plain text and public key.
Any help is greatly appreciated.
Thanks in Advance,
Sandeep
I need to generating a RSA and DSA key pair (public and private key) in PEM format using java.
I want the public and private key files to be opened with this format:
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAryQICCl6NZ5gDKrnSztO
3Hy8PEUcuyvg/ikC+VcIo2SFFSf18a3IMYldIugqqqZCs4/4uVW3sbdLs/6PfgdX
7O9D22ZiFWHPYA2k2N744MNiCD1UE+tJyllUhSblK48bn+v1oZHCM0nYQ2NqUkvS
j+hwUU3RiWl7x3D2s9wSdNt7XUtW05a/FXehsPSiJfKvHJJnGOX0BgTvkLnkAOTd
OrUZ/wK69Dzu4IvrN4vs9Nes8vbwPa/ddZEzGR0cQMt0JBkhk9kU/qwqUseP1QRJ
5I1jR4g8aYPL/ke9K35PxZWuDp3U0UPAZ3PjFAh+5T+fc7gzCs9dPzSHloruU+gl
FQIDAQAB
-----END PUBLIC KEY-----
My public key is already generated before with this format that i do not want it:
0Ÿ0 *†H†÷ 0Ÿ0 *†H†÷
ok, this is my code of key generation:
private static void createKey()
throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Password to encrypt the private key: ");
String password = in.readLine();
System.out.println("Generating an RSA keypair...");
// Create an RSA key
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
System.out.println("Done generating the keypair.\n");
// Now we need to write the public key out to a file
System.out.print("Public key filename: ");
String publicKeyFilename = "C:/Users/Joe/Desktop/" + in.readLine();
// Get the encoded form of the public key so we can
// use it again in the future. This is X.509 by default.
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
// Write the encoded public key out to the filesystem
FileOutputStream fos = new FileOutputStream(publicKeyFilename);
fos.write(publicKeyBytes);
fos.close();
// Now we need to do the same thing with the private key,
// but we need to password encrypt it as well.
System.out.print("Private key filename: ");
String privateKeyFilename = "C:/Users/Joe/Desktop/" + in.readLine();
// Get the encoded form. This is PKCS#8 by default.
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
// Here we actually encrypt the private key
byte[] encryptedPrivateKeyBytes =
passwordEncrypt(password.toCharArray(),privateKeyBytes);
fos = new FileOutputStream(privateKeyFilename);
fos.write(encryptedPrivateKeyBytes);
fos.close();
}
thank you for your help..
Instead of manually generating the PEM String, you can use the bouncy castle to do it for you: since it's a tested library you can be sure about the output. The following code is in Kotlin but can easily be used with Java syntax:
val gen = KeyPairGenerator.getInstance("RSA")
gen.initialize(2048)
val pair = gen.generateKeyPair()
val privateKey: PrivateKey = pair.private
val pemObject = PemObject("RSA PRIVATE KEY", privateKey.encoded)
val byteStream = ByteArrayOutputStream()
val pemWriter = PemWriter(OutputStreamWriter(byteStream))
pemWriter.writeObject(pemObject)
pemWriter.close();
println(String(byteStream.toByteArray()))
Maybe a bit late but there is my solution. Hope it helps others.
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
Here you're taking bytes of key and writing directly to file. So you get the appropriate result - DER-encoded file. However PEM is Base64 encoded format with line breaks each 64 symbols and header/footer.
There is code implementing this logic:
String publicKeyContent = Base64.getEncoder().encodeToString(publicKeyBytes);
String publicKeyFormatted = "-----BEGIN PUBLIC KEY-----" + System.lineSeparator();
for (final String row:
Splitter
.fixedLength(64)
.split(publicKeyContent)
)
{
publicKeyFormatted += row + System.lineSeparator();
}
publicKeyFormatted += "-----END PUBLIC KEY-----";
So publicKeyFormatted will contain PEM-encoded string of public key.
P.S. Splitter is a class provided in Guava lib, but you can split the string with a simple cycle or somehow.
I've written a library that includes methods which can do this. It's called Reasonably Easy Cryptography and you can use the PEMHandler methods for this. Here's how you could do it, assuming you've imported PEMHandler from my library, and that key's class is an implementation of java.security.Key such as a PrivateKey:
String pem = PEMHandler.keyToPem(key);
This works with any kind of Key, it'll figure out whether it's a public or private key and what algorithm it uses on its own (it isn't perfect and I'm still working on finding the best way to do that, but it does work fine with PrivateKey and PublicKey). There's also a method to do this for both keys in a KeyPair in one call, and methods to convert the PEM string back to a Key.
I'm writing a java code to generate keys and save them in files, I am using BouncyCastle library to write the privatekey into .pem file using pemwriter(if it is in PKCS#1) and using a regular FileOutputStream to export it into PKCS#8.
Now when exporting into DER, the problem come when trying to export it in PKCS#1.
I searched a lot but cannot find a suitable way to encode the privatekey in PKCS#1 or to convert the regular encoding of java privatekey's (PKCS#8) to PKCS#1, or if you can guide me to convert PrivateKey to RSAPrivateKey or DSAPrivateKey or ECPrivateKey. Here is a snippet of my code to export
JcePEMEncryptorBuilder builder = new JcePEMEncryptorBuilder("DES-EDE3-CBC");
PEMEncryptor enc = builder.build(password);
FileOutputStream fis = new FileOutputStream(new File(privatekey.der));
if (isPKCS8) {
if (!encrypt) {
fis.write(privateKeyBytes);
} else {
fis.write(enc.encrypt(privateKeyBytes));
}
fis.flush();
fis.close();
where privateKeyBytes are the returned bytes of PrivateKey.getEncoded(). they are in PKCS#8 and if I can convert PrivateKey to RSAPrivateKey or DSAPrivateKey they represent the private key in PKCS#1 format
Apparently you can use type information and perform class casting
PrivateKey privKey = ...;
if (privKey instance of RSAPrivateKey) {
RSAPrivateKey rsaPrivKey = (RSAPrivateKey)privKey;
if (privKey instance of DSAPrivateKey) {
DSAPrivateKey dsaPrivKey = (DSAPrivateKey)privKey;
if (privKey instance of ECPrivateKey) {
ECPrivateKey ecPrivKey = (ECPrivateKey)privKey;
}
I would like to use id_rsa and id_rsa.pub to create a challenge-response login system for an application in Java. Towards this purpose, I want to be able to construct PublicKey and PrivateKey from id_rsa and id_rsa.pub.
The direct approach would be to parse these in the way I would normally parse a text file, and then manually construct the appropriate java.security data structures for signing and verifying in the client and server.
Is there a standard library shortcut that will ingest these files directly?
The closest and easiest way I know, would be to use The Legion of the Bouncy Castle Java API with something like this -
// Just for the public / private key files...
private static String readFileAsString(
String filePath) throws java.io.IOException {
StringBuilder sb = new StringBuilder(100);
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(
filePath));
int fileIn;
while ((fileIn = reader.read()) != -1) {
sb.append((char) fileIn);
}
} finally {
reader.close();
}
return sb.toString();
}
// Add the SecurityProvider and a Base64 Decoder (Once)
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
BASE64Decoder b64 = new BASE64Decoder();
// For the publicKey
String publicKeyString = readFileAsString(publicKeyFileName);
AsymmetricKeyParameter publicKey =
(AsymmetricKeyParameter) PublicKeyFactory.createKey(b64.decodeBuffer(publicKeyString));
// For the privateKey
String privateKeyString = readFileAsString(privateKeyFilename);
AsymmetricKeyParameter privateKey =
(AsymmetricKeyParameter) PrivateKeyFactory.createKey(b64.decodeBuffer(privateKeyString));
yes, I just wrote it: openssh-java. :-)