I am trying to read data from password protected PEM file to generate public and private key from those data. I have the password and I need to pass that password during opening the file. But I haven't any option in java to do that.
Need emergency assistance on this. It would have been much better if I could get a code example for this.
EDITED:
My current code is -
public void readPrivateKey(String filePath)
{
File fp = new File(filePath);
FileInputStream fis = new FileInputStream(fp);
DataInputStrean dis = new DataInputStream(fis);
byte [] keyBytes = new byte [(int) fp.length()];
dis.readFully(keyBytes);
dis.close();
String temp = new String(keyBytes);
String privateKeyPem = removeUnnecessaryTags(temp); //this function returns a string after removing String like this "BEGIN......."
byte[] decoded = Base64.decode(privateKeyPem);
ASN1Sequence primitive = (ASN1Sequence) ASN1Sequence.fromByteArray(decoded);
Enumeration <?> e = primitive.getObjects();
BigInteger bigInt = ((DERInteger)e.NextElement()).getValue();
int version = bigInt.intValue();
BigInteger modulus = ((DERInteger)e.NextElement()).getValue();
BigInteger publicExp = ((DERInteger)e.NextElement()).getValue();
//... all the values has been retrieved in this way
}
I believe you are taking about RSA key-pair.
You can do it without using Java like this,
openssl rsa -in MYFILE.pem -pubout > MYFILE.pub
ssh-keygen -f MYFILE.pub -i -m PKCS8
If you need to do this in Java, refer to link given by #m0skit0 in comments.
Related
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).
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 have certificate created using java class CertAndKeyGen and X500Name and I am able to generate the certificate which is in byte array. Now I want the private key I used in certificate and convert it into readable format. Below is the code I used to create the certificate,
CertAndKeyGen keypair = new CertAndKeyGen("RSA", "SHA1WithRSA", null);
X500Name x500Name = new X500Name(commonName, organizationalUnit, organization, city, state, country);
keypair.generate(keysize);
PrivateKey privKey = keypair.getPrivateKey();
PKCS10 certReq = keypair.getCertRequest(x500Name);
X509Certificate[] chain = new X509Certificate[1];
chain[0] = keypair.getSelfCertificate(x500Name, new Date(), (long) validity * 24 * 60 * 60);
keyStore.setKeyEntry(alias, privKey, keyStorePassword.toCharArray(), chain);
ByteArrayOutputStream bs = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(bs);
certReq.print(ps);
byte[] certReqPrintable = bs.toByteArray();
I have got no clues, please help me to go in right direction to get private key and convert it into readable format. Thanks in advance.
If you want to save the private key to a file use
byte[] privateKeyBytes = privKey.getEncoded();
This returns the key in DER encoded (binary) format.
In case you want just to display the contained values on the console just print it using toString():
System.out.println(privKey);
BouncyCastle has the useful PEMWriter class that you can use to write the private key to a file in PEM format (this is what tools like OpenSSH and curl expect).
PEMWriter privatepemWriter = new PEMWriter(new FileWriter(filename)));
privatepemWriter.writeObject(privKey);
privatepemWriter.close();
Otherwise you can just save the byte array from the private key which is the DER format also used by many tools.
Finally you can write it to a JKS keystore used by other java programs using this:
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null);
keyStore.setKeyEntry("some alias", privKey, somePassword.toCharArray(), chain[0]));
FileOutputStream fos = new FileOutputStream(filename);
keyStore.store(fos, somePassword.toCharArray());
fos.close();
I remember do this long time ago with OpenSSL, but I want to know if it's possible and how, I've never used Cryptography on java.
The assumption is that we are talking about RSA private and Public keys. Then, if you are working from a PEM format file, then first you need to read the private key from the file into a PrivateKey object:
public PrivateKey readPemRsaPrivateKey(String pemFilename) throws
java.io.IOException,
java.security.NoSuchAlgorithmException,
java.security.spec.InvalidKeySpecException
{
String pemString = File2String(pemFilename);
pemString = pemString.replace("-----BEGIN RSA PRIVATE KEY-----\n", "");
pemString = pemString.replace("-----END RSA PRIVATE KEY-----", "");
byte[] decoded = Base64.decodeBase64(pemString);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(keySpec);
}
where File2String is something like:
private static String File2String(String fileName) throws
java.io.FileNotFoundException, java.io.IOException
{
File file = new File(fileName);
char[] buffer = null;
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
buffer = new char[(int)file.length()];
int i = 0;
int c = bufferedReader.read();
while (c != -1) {
buffer[i++] = (char)c;
c = bufferedReader.read();
}
return new String(buffer);
}
Now you can generate the corresponding PublicKey with code like this:
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.RSAPublicKeySpec;
...
PrivateKey myPrivateKey = readPemRsaPrivateKey(myPrivateKeyPemFileName);
RSAPrivateCrtKey privk = (RSAPrivateCrtKey)myPrivateKey;
RSAPublicKeySpec publicKeySpec = new java.security.spec.RSAPublicKeySpec(privk.getModulus(), privk.getPublicExponent());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey myPublicKey = keyFactory.generatePublic(publicKeySpec);
Credits: How to get a RSA PublicKey by giving a PrivateKey?
Please make sure that Eli Rosencruft answer is basically correct, but the order of the modulus and the public exponent are incorrect! This is the correct statement:
RSAPublicKeySpec publicKeySpec = new java.security.spec.RSAPublicKeySpec(privk.getModulus(), privk.getPublicExponent());
You cannot generate either key directly from the other. It is mathematically impossible. If you had a key blob that contained both the public and private keys, you could extract either one of them with relative ease.
EDIT, 2017: Many years and a much better understanding of crypto later, and it's now clear to me that this answer isn't really correct.
To quote Wikipedia:
The public key consists of the modulus n and the public (or encryption) exponent e. The private key consists of the modulus n and the private (or decryption) exponent d, which must be kept secret. p, q, and λ(n) must also be kept secret because they can be used to calculate d.
The public modulus n can be computed as p × q. The only thing missing from a raw private key is e, but this value is usually selected as 65537, and if not you can still compute e from d and λ(n).
However, many private key storage formats actually contain the public modulus n alongside the other components, so you can just do a direct extraction of the values.
EDIT, 2018: Still getting downvotes for this, and rightly so! I'm leaving this answer up so people can see why I was originally wrong, and to remind myself not to be wrong in future.
I have some .net code that generates a SHA1 hash on an xml document and need it to match a SHA1 hash generated on the same xml document in java code. The xml doc is sent to the java system and they generate a hash and match against the one i send to verify they are getting the document I intended them to. Below are the snippets in use for this and they both consistently generate the same different hashes. Any ideas why the following code would not generate the same hash?
.NET
String fileName = #"D:\Projects\CHIEP\hasherror\cdadoc.xml";
byte[] buff = null;
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
BinaryReader br = new BinaryReader(fs);
long numBytes = new FileInfo(fileName).Length;
buff = br.ReadBytes((int)numBytes);
HashAlgorithm hash = new SHA1Managed();
byte[] hashBytes = hash.ComputeHash(buff);
string hex = BitConverter.ToString(hashBytes);
hex = hex.Replace("-", "").ToLower();
Java
public static String SHA1(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest md;
md = MessageDigest.getInstance("SHA-1");
byte[] sha1hash = new byte[40];
md.update(text.getBytes("UTF-8"), 0, text.length());
sha1hash = md.digest();
//String converted = convertToHex(sha1hash);
String converted = getHexString(sha1hash);
return converted;
}
.NET output
587691443778c1da54c3fd04bb35ec68a5a7fecd
Java output:
89665a8268d7d1901aba529dc8c9cea0f910c1bd
The input is a UTF-8 encoded CDA document that gets created here:
XmlSerializer serializer = new XmlSerializer(obj.GetType());
System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, new UTF8Encoding(false));
XmlDocument xmlDoc = new XmlDocument();
serializer.Serialize(xmlTextWriter, obj);
memoryStream = (System.IO.MemoryStream)xmlTextWriter.BaseStream;
String xml = UTF8Encoding.UTF8.GetString(memoryStream.ToArray());
UPDATE:
Getting close to a solution. I found in the document there is a character that is being interpreted differently in the java than in the .net code.
Java reads in this:
value="21.9456" unit="kg/m²"
.net reads in this:
value="21.9456" unit="kg/m²"
If I open in the xml editor of my choice it looks like the what .net reads in. I suspect it has something to do with java doing a conversion and .net simply assuming..
I had the following java code:
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] digest = md.digest(password);
and I converted it to C# as follows:
var sha1 = SHA1Managed.Create();
byte[] outputBytes = sha1.ComputeHash(password);
In order to get my hashes to match, I did NOT include the following code that you had in your example:
string hex = BitConverter.ToString(hashBytes);
hex = hex.Replace("-", "").ToLower();