Get public key from private in Java - java

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.

Related

Given two large primes how do I obtain a tor onion address using java

Given two large primes and a public exponent I'd like to calculate the onion address of a tor service using java. Given the following code:
//a couple of large primes from https://primes.utm.edu/lists/small/small2.html
BigInteger p = new BigInteger("14083359469338511572632447718747493405040362318205860500297736061630222431052998057250747900577940212317413063");
BigInteger q = new BigInteger("76921421106760125285550929240903354966370431827792714920086011488103952094969175731459908117375995349245839343");
BigInteger publicExponent = new BigInteger("65537");
//get the private key
RSAPrivateCrtKeySpec crtKeySpec = new RSAPrivateCrtKeySpec(modulus, publicExponent, privateExponent, p, q, primeExponentP, primeExponentQ, crtCoefficient);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(crtKeySpec);
System.out.println(getPrivateKeyString(privateKey));
//get the public key -> onion address
RSAPublicKeySpec publicSpec = new RSAPublicKeySpec(modulus, publicExponent);
PublicKey publicKey = keyFactory.generatePublic(publicSpec);
System.out.println(getOnionAddress(publicKey));
Using the following method:
public static String getPrivateKeyString(PrivateKey privateKey) {
Base64.Encoder encoder = Base64.getEncoder(); //using import java.util.Base64;
String privateKeyEncoded = encoder.encodeToString(privateKey.getEncoded());
StringBuffer sb = new StringBuffer();
sb.append("-----BEGIN PRIVATE KEY-----\n");
sb.append(privateKeyEncoded);
sb.append("\n-----END PRIVATE KEY-----\n");
String result = sb.toString();
return result;
}
I get the hostname of xvufqn6goj4qh5uc. However when I use the generated private key in a tor service the hostname generated is 6qbn26dres64uon3.onion.
I have previously asked a similar question, but I think my public key is in a different encoding and have tried different massaging to get the expected result but to no avail.
How do I generate the correct onion address in my code?

Creating RSA Public Key From String

I've generated this test public key using 1024 RSA and then encoded it to DER and Base64 in another coding platform. I copied the key into a string in Android/Eclipse and I am trying to turn it into a public key using KeyFactory. It just keeps giving me an InvalidKeySpecException no matter what I try. Any advice at all would be appreciated.
private void prepKeys() {
String AppKeyPub = "MIGHAoGBAOX+TFdFVIKYyCVxWlnbGYbmgkkmHmEv2qStZzAFt6NVqKPLK989Ow0RcqcDTZaZBfO5" +
"5JSVHNIKoqULELruACfqtGoATfgwBp4Owfww8M891gKNSlI/M0yzDQHns5CKwPE01jD6qGZ8/2IZ" +
"OjLJNH6qC9At8iMCbPe9GeXIPFWRAgER";
// create the key factory
try {
KeyFactory kFactory = KeyFactory.getInstance("RSA");
// decode base64 of your key
byte yourKey[] = Base64.decode(AppKeyPub,0);
// generate the public key
X509EncodedKeySpec spec = new X509EncodedKeySpec(yourKey);
PublicKey publicKey = (PublicKey) kFactory.generatePublic(spec);
System.out.println("Public Key: " + publicKey);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The key you have is in PKCS#1 format instead of SubjectPublicKeyInfo structure that Java accepts. PKCS#1 is the encoding of the RSA parameters only and lacks things such as an algorithm identifier. SubjectPublicKeyInfo uses PKCS#1 internally - for RSA public keys anyway.
As the PKCS#1 public key is at the end of the SubjectPublicKeyInfo structure it is possible to simply prefix the bytes so that they become an RSA SubjectPublicKeyInfo. That solution is easier to perform without additional libraries such as Bouncy Castle. So if you need to go without an external library then you may have a look at my answer here.
Alternatively a simple BER decoder could be written to decode the structure into the two BigInteger values. The structure itself is not that complicated but the BER/DER length encoding takes some getting used to.
However, you can also use Bouncy Castle (lightweight API) to solve your issues:
String publicKeyB64 = "MIGHAoGBAOX+TFdFVIKYyCVxWlnbGYbmgkkmHmEv2qStZzAFt6NVqKPLK989Ow0RcqcDTZaZBfO5"
+ "5JSVHNIKoqULELruACfqtGoATfgwBp4Owfww8M891gKNSlI/M0yzDQHns5CKwPE01jD6qGZ8/2IZ"
+ "OjLJNH6qC9At8iMCbPe9GeXIPFWRAgER";
// ok, you may need to use the Base64 decoder of bouncy or Android instead
byte[] decoded = Base64.getDecoder().decode(publicKeyB64);
org.bouncycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(decoded);
BigInteger modulus = pkcs1PublicKey.getModulus();
BigInteger publicExponent = pkcs1PublicKey.getPublicExponent();
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey generatedPublic = kf.generatePublic(keySpec);
System.out.printf("Modulus: %X%n", modulus);
System.out.printf("Public exponent: %d ... 17? Why?%n", publicExponent); // 17? OK.
System.out.printf("See, Java class result: %s, is RSAPublicKey: %b%n", generatedPublic.getClass().getName(), generatedPublic instanceof RSAPublicKey);
As you can see it actually only requires a single class as interface, although that is of course backed up with the entire ASN.1/BER decoder functionality within Bouncy Castle.
Note that it may be required to change the Base 64 decoder to the Android specific one (android.util.Base64). This code was tested on an equivalent Java runtime.
For those who dont want to use Bouncy Castle
public class RSAKeySeperation {
public static void main(String[] args) throws InvalidKeySpecException, NoSuchAlgorithmException {
String publicKeyB64 = "MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQBV8xakN/wOsB6qHpyMigk/5PrSxxd6tKTJsyMIq5f9npzZue0mI4H2o8toYImtRk6VHhcldo0t7UwsQXmFMk7D"
+ "i3C53Xwfk7yEFSkXGpdtp/7fbqNnjVoJl/EPcgoDsTPrHYF/HgtmbhzuYvYeY1zpV0d2uYpFxAuqkE9FreuuH0iI8xODFe5NzRevXH116elwdCGINeAecHKgiWe"
+ "bGpRPml0lagrfi0qoQvNScmi/WIN2nFcI3sQFCq3HNYDBKDhO0AEKPB2FjvoEheJJwTs5URCYsJglYyxEUon3w6KuhVa+hzYJUAgNTCsrAhQCUlX4+5LOGlwI5gonm1DYvJJZAgMBAAEB";
byte[] decoded = Base64.getDecoder().decode(publicKeyB64);
X509EncodedKeySpec spec =
new X509EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPublicKey generatePublic = (RSAPublicKey) kf.generatePublic(spec);
BigInteger modulus = generatePublic.getModulus();
System.out.println(modulus);
BigInteger exponent = generatePublic.getPublicExponent();
System.out.println(exponent);
}
}

Use of ECC in Java SE 1.7

I am trying to run the code in this pdf. For example, for ECCKeyGeneration, I get the following output instead of Figure 1 in the pdf:
sun.security.ec.ECPrivateKeyImpl#58b6
Sun EC public key, 192 bits
public x coord: 4812174841545539052388802454891896756539688756781766645984
public y coord: 1161396487043052042009627836016170768650083444786081272028
parameters: secp192r1 [NIST P-192, X9.62 prime192v1] (1.2.840.10045.3.1.1)
The private key doesn't printed to console/screen. Instead, it says "sun.security.ec.ECPrivateKeyImpl#blabla" as you can see above. What could be the reason for this situation?
Here is the code if you like to test:
import java.security.*;
import java.security.spec.*;
public class ECCKeyGeneration {
public static void main(String[] args) throws Exception {
KeyPairGenerator kpg;
kpg = KeyPairGenerator.getInstance("EC","SunEC");
ECGenParameterSpec ecsp;
ecsp = new ECGenParameterSpec("secp192r1");
kpg.initialize(ecsp);
KeyPair kp = kpg.genKeyPair();
PrivateKey privKey = kp.getPrivate();
PublicKey pubKey = kp.getPublic();
System.out.println(privKey.toString());
System.out.println(pubKey.toString());
}
}
Normally private key values are not printed to screen. Hence there is little sense to provide a toString() for ECPrivateKey (a sub-class of PrivateKey).Printing out private key values is of course not safe.
It is of course possible to print out the secret part of the private key; printing out the other parameters of secp192r1 makes little sense. You can easily retrieve them from standard documents from NIST or Certicom if required.
ECPrivateKey ecPrivKey = (ECPrivateKey) eckp.getPrivate();
System.out.println(ecPrivKey.getS().toString(16));
Note that you are better off printing out a hash over the private key value S if you just need this for verifying that the right private key is used.
Your Problem is with the toString() methode of the java PrivateKey class.
toString() does not necessarily returns all the data in your object, just a string representation.
So in your case you could use the getEncoded() methode.
Note that your byte[] might contain unprintable characters, so you may need to convert it in a readable form f.e. Base64.
PrivateKey privKey = kp.getPrivate();
byte[] data = privKey.getEncoded();
//first methode, convert to string
String privKeyString = new String(data);
System.out.println(privKeyString);
//second methode, print all byte values
for(byte value : data)
System.out.println(value);
See also print byte array
Also you can use:
System.out.println(Arrays.toString(kp.getPrivate().getEncoded()));

how to generate public and private key in PEM format

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.

Looking for Signing algorithm that creates 32 or 16 byte keys in Java

Cannot match up the size of key generated using public/private keys for licensing application. Ive written a self contained example that creates public/private key, create a license by signing user emailaddress with public key, and then check using public key, license and email address that the license indeed was encoded using private key (Obviously this wouldn't all be in one class usually).
This all works but the hex version of the license key is 96 characters (i.e representing 48 bytes/384 bits) which is a little longer than I wanted (In contrast the length of public/private keys is not a problem and the longer the better). What could I use to generate a 32 (64 hex chars) byte or maybe 16 byte (32 hex chars), and would the security of this be reasonable ?
Picking another algorithm is somewhat hard as I do not understand the the interaction between the algorithm picked for generating the keys
KeyPairGenerator.getInstance("DSA");
and the algorithm for signing
Signature.getInstance("SHA/DSA");
and I cant find a list for either.
One other point when I generate a public/private key pairs I specify key size of
keyGen.initialize(1024, new SecureRandom());
yet neither the public key (443 bytes) or the private key (335 bytes) or the sum of both (778 bytes) match this number.
import org.apache.commons.codec.binary.Hex;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
*
*/
public class CreateLicense
{
private String PUBLIC_KEY;
private String PRIVATE_KEY;
public static void main(final String[] args)
{
try
{
String email = args[0];
System.out.println("Creating license for:"+email);
CreateLicense cl = new CreateLicense();
cl.generatePublicPrivateKeyPair();
String license = cl.createLicense(email);
cl.checkLicense(email, license);
}
catch(Throwable t)
{
t.printStackTrace();
}
}
//Would only be done once on server
private void generatePublicPrivateKeyPair() throws Exception
{
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
keyGen.initialize(1024, new SecureRandom());
final KeyPair pair = keyGen.generateKeyPair();
PrivateKey privateKey = pair.getPrivate();
PRIVATE_KEY=Hex.encodeHexString(privateKey.getEncoded());
PublicKey publicKey = pair.getPublic();
PUBLIC_KEY=Hex.encodeHexString(publicKey.getEncoded());
System.out.println("PrivateKeyHexLength:"+privateKey.getEncoded().length);
System.out.println("PublicKeyHexLength:"+publicKey.getEncoded().length);
}
private PrivateKey reinflatePrivateKey(String keyAsHexString) throws Exception
{
byte[] keyBytes = Hex.decodeHex(keyAsHexString.toCharArray());
final PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(keyBytes);
final KeyFactory keyFactory = KeyFactory.getInstance("DSA");
final PrivateKey privateKey = keyFactory.generatePrivate(privKeySpec);
return privateKey;
}
private PublicKey reinflatePublicKey(String keyAsHexString) throws Exception
{
byte[] keyBytes = Hex.decodeHex(keyAsHexString.toCharArray());
final X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(keyBytes);
final KeyFactory keyFactory = KeyFactory.getInstance("DSA");
final PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
return publicKey;
}
//License Create on server based on email address
private String createLicense(String emailAddress) throws Exception
{
String message=emailAddress;
PrivateKey privateKey = reinflatePrivateKey(PRIVATE_KEY);
final Signature dsa = Signature.getInstance("SHA/DSA");
dsa.initSign(privateKey);
dsa.update(message.getBytes());
final byte[] m1 = dsa.sign();
String license = Hex.encodeHexString(m1);
System.out.println("CreateLicense:"+license+":Size:"+license.length());
return license;
}
//Client checks that given known emailaddress and public key that a if a license was derived from
//that and corresponding privatekey it would match license.
private boolean checkLicense(String emailAddress, String license) throws Exception
{
String message=emailAddress;
PublicKey publicKey = reinflatePublicKey(PUBLIC_KEY);
final Signature dsa = Signature.getInstance("SHA/DSA");
dsa.initVerify(publicKey);
dsa.update(message.getBytes());
boolean result = dsa.verify(Hex.decodeHex(license.toCharArray()));
System.out.println("Result"+result);
return result;
}
}
gives output like
Creating license for:testuser#nowhere.com
PrivateKeyHexLength:335
PublicKeyHexLength:443
CreateLicense:302c021425f7ad7289b073f82a1d808838f43e0134c5591402140d2a7a4e3967706d4659dc73ace6455040a5fc6b:Size:92
Resulttrue
#Paul - I think your solution here would be to use ECDSA.
Change your line of code
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
to
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ECDSA");
The keys are much shorter than DSA - and I'm sure hex version signature would be shorter. I suggest you use a prime ECC curve of say 256 or 128 bits.
Please let us know if this solves the problem.

Categories

Resources