I need to save my RSA private and public key. Here I create it.
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair kp = kpg.genKeyPair();
publicKey = kp.getPublic();
privateKey = kp.getPrivate();
Now I want to use both keys the next time. So they should be stored on the device.
byte[] publicKeyBytes = publicKey.getEncoded();
byte[] privateKeyBytes = privateKey.getEncoded();
So I can get the Byte Arrays and then I save them as a text file.
Then the next time i read it and convert it back to the byte Array.
Now my question: How can i convert a byte array back to a key?
This does not really work:
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKeyBytes);
publicKey = keyFactory.generatePublic(spec);
spec = new X509EncodedKeySpec(privateKeyBytes);
privateKey = keyFactory.generatePrivate(spec);
On LogCat, this is going to be display:
Must use RSAPublicKeySpec or PKCS8EncodedKeySpec; was
java.security.spec.X509EncodedKeySpec
Any ideas whats wrong with this code?
Thanks for your help.
Try using PKCS8EncodedKeySpec for the private key.
RSAPublicKeySpec for the public key, instead of X509EncodedKeySpec for both.
Please also note that saving keys to a text file might not be the most secure idea. Android provides a nice KeyStore Api, which is easy to use.
You try encode the keys in Base64 and stored like a String, when you retrieved only need decoded the keys from Base64 to Byte arrays.
Related
I have an issue with my java code. I'm trying to encrypt a file. However, when I run my java code I get "java.security.InvalidKeyException: Invalid AES key length: 162 bytes".
Here is the code:
byte[] rawFile;
File f = new File("./src/wonkybox.stl");
FileInputStream fileReader = new FileInputStream(f);
rawFile = new byte[(int)f.length()];
fileReader.read(rawFile);
/***** Encrypt the file (CAN DO THIS ONCE!) ***********/
//Generate the public/private keys
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("AES");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG","SUN");
keyGen.initialize(1024, random);
KeyPair key = keyGen.generateKeyPair();
PrivateKey privKey = key.getPrivate();
PublicKey pubKey = key.getPublic();
//Store the keys
byte[] pkey = pubKey.getEncoded();
FileOutputStream keyfos = new FileOutputStream("./CloudStore/keys/pubkey");
keyfos.write(pkey);
keyfos.close();
pkey = privKey.getEncoded();
keyfos = new FileOutputStream("./CloudStore/keys/privkey");
keyfos.write(pkey);
keyfos.close();
//Read public/private keys
KeyFactory keyFactory = KeyFactory.getInstance("AES");
FileInputStream keyfis = new FileInputStream("./CloudStore/keys/pubkey");
byte[] encKey = new byte[keyfis.available()];
keyfis.read(encKey);
keyfis.close();
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encKey);
PublicKey pub1Key = keyFactory.generatePublic(pubKeySpec);
keyfis = new FileInputStream("./CloudStore/keys/privkey");
encKey = new byte[keyfis.available()];
keyfis.read(encKey);
keyfis.close();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(encKey);
PrivateKey priv1key = keyFactory.generatePrivate(privKeySpec);
//Encrypt file using public key
Cipher cipher = Cipher.getInstance("AES");
System.out.println("provider= " + cipher.getProvider());
cipher.init(Cipher.ENCRYPT_MODE, pub1Key);
byte[] encryptedFile;
encryptedFile = cipher.doFinal(rawFile);
//Write encrypted file to 'CloudStore' folder
FileOutputStream fileEncryptOutput = new FileOutputStream(new File("./CloudStore/encrypted.txt"));
fileEncryptOutput.write(encryptedFile);
fileEncryptOutput.close();
The error occurs at the line "KeyPairGenerator keyGen = KeyPairGenerator.getInstance("AES");".
AES is a symmetric algorithm, hence they use of KeyPairGenerator is not supported. To generate a key with AES you call KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); //set keysize, can be 128, 192, and 256
By looking at the rest of your code, it looks like you are trying to achive asymmetric encryption (since you call getPublic() and getPrivate() etc), so I advice you to switch to using RSA or any other asymmetric algorithm that java supports. You will most likley only need to replace AES with RSA in your getInstance(); calls, and pherhaps some fine-tuning. Good luck
As far as I know, AES is symmetric encryption algorithm i.e. it needs only one key for encryption/decryption.
From the JavaDoc of java.security.KeyPairGenerator:
The KeyPairGenerator class is used to generate pairs of public and private keys.
Meaning that it should be used for asymmetric encryption algorithms. For symmetric encryption algorithms one should use javax.crypto.KeyGenerator.
However, I advise simply mimicking some tutorial on how to encrypt / decrypt byte array in Java using AES like this one.
It uses sun.misc.Base64Encoder / Base64Decoder classes to encode / decode byte array to / from String, however you may skip this step.
Hope this helps
How can you use a keypair generator for AES? AES is a symmetric key algorithm. Refer this link. That means if you encrypt data using a key "k", then you will have to decrypt it also using the same key "k". But when you generate key pair, as the name suggests, two keys are generated and if you encrypt using one of the keys, you can decrypt only using the other key. This is the base for PKI.
If you want to use keypair generator use an algorithm like "rsa" or "dsa" in the getInstance() method like this :
KeyPairGenerator keygen=KeyPairGenerator.getInstance("rsa");
I think your code should now work fine after making the above change.
I am generating a keypair and converting one of the same into string which later is inserted into the database using the following code:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair generatedKeyPair = keyGen.genKeyPair();
PublicKey pubkey = generatedKeyPair.getPublic();
PrivateKey prvkey = generatedKeyPair.getPrivate();
System.out.println("My Public Key>>>>>>>>>>>"+pubkey);
System.out.println("My Private Key>>>>>>>>>>>"+prvkey);
String keyAsString = new BigInteger(prvkey.getEncoded()).toString(64);
I then retrieve the string from the database and convert it back to the original key using the following code (where rst is my ResultSet):
String keyAsString = rst.getString("privateKey").toString();
byte[] bytes = new BigInteger(keyAsString, 64).toByteArray();
//byte k[] = "HignDlPs".getBytes();
PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(bytes);
KeyFactory rsaKeyFac = KeyFactory.getInstance("RSA");
PrivateKey privKey = rsaKeyFac.generatePrivate(encodedKeySpec);
On using the privKey for RSA decryption, I get the following exception
java.lang.NumberFormatException: Radix out of range
at java.math.BigInteger.<init>(BigInteger.java:294)
at com.util.SimpleFTPClient.downloadFile(SimpleFTPClient.java:176)
at com.Action.FileDownload.processRequest(FileDownload.java:64)
at com.Action.FileDownload.doGet(FileDownload.java:94)
Please guide.
You're getting an exception because the radix you're providing is greater than Character.MAX_RADIX (which is 36). In other words, it's entirely predictable.
Don't use BigInteger as an encoding class. That's not what it's there for. There are plenty of decent ways of performing base64 encoding. Personally I like this public domain library.
String keyAsString = Base64.encode(prvkey.getEncoded());
Then later:
byte[] bytes = Base64.decode(keyAsString);
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair kp = kpg.genKeyPair();
Key publicKey = kp.getPublic();
Key privateKey = kp.getPrivate();
I want to create just the public key from a byte[].
I have tried this as an experiment:
publicKey = new SecretKeySpec(publicKey.getEncoded(), publicKey.getAlgorithm());
But decryption using that key then fails.
I have also tried serializing the key with ObjectOutputStream, but serialization fails.
java.io.NotSerializableException: org.apache.harmony.xnet.provider.jsse.OpenSSLKey
I read here that I can't use SecretKeySpec with RSA.
so as long as you are talking of a SecretKey and not an RSA or DSA key then you don't have to go through any contortions involving KeyGenerator or the like.
Anyone know how to perform these contortions or a way of doing this.
Asymmetric keys like those from RSA are usually stored in X509 format. Therefor you can use X509EncodedKeySpecinstead.
A simple example is already in the Java 7 JavaDoc (just replace DSA with RSA):
http://docs.oracle.com/javase/7/docs/api/java/security/KeyFactory.html
X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(bobEncodedPubKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey bobPubKey = keyFactory.generatePublic(bobPubKeySpec);
If you need to deserialize the private from byte[], I've found that you must use PKCS8EncodedKeySpec.
Depends on what do you want to do with the serialized representation. If the consumer is no one but your own program, feel free to roll your own implementation. A public RSA key consists of two integers - exponent and modulus. Modulus is large - around 1024 bits, exponent is typically on the order of 17 bits. Both are available as BigInteger objects if you cast your public key object to RSAPublicKey.
So, to recap:
RSAPublicKey publicKey = (RSAPublicKey)kp.getPublic();
return publicKey.getModulus().toString() + "|" +
publicKey.getPublicExponent().toString();
That's sufficient to restore the key. To deserialize:
String []Parts = MyKeyString.split("\\|");
RSAPublicKeySpec Spec = new RSAPublicKeySpec(
new BigInteger(Parts[0]),
new BigInteger(Parts[1]));
return KeyFactory.getInstance("RSA").generatePublic(Spec);
If the key needs to be passed to third party software, you better serialize to a standard format - PEM or DER.
I use the following code to convert a PubliKey to PEM Base64 format
String publicKeyString = javax.xml.bind.DatatypeConverter.printBase64Binary(publicKey.getEncoded());
I hope it helps.
I'm writing a java program to import private keys from files within the file system and make a private key object, using java...
I could do it for files in .pem format but, with .der format, I had no idea what to do, since I couldnt firstly detect the algorithm used to generate the keys.
within .pem files I could determine the algorithm from the header for PKCS#1 which have a header like
-----BEGIN RSA PRIVATE KEY----
formats and used the bouncycastle pem reader for those in PKCS#8 which have a header
-----BEGIN PRIVATE KEY-----
but with those in .der format no idea :(
also if anyone have an idea about .key format tell me
thanx
If your DER files are in PKCS#8 format, you can use the Java KeyFactory and do something like this:
// Read file to a byte array.
String privateKeyFileName = "C:\\myPrivateKey.der";
Path path = Paths.get(privateKeyFileName);
byte[] privKeyByteArray = Files.readAllBytes(path);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privKeyByteArray);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey myPrivKey = keyFactory.generatePrivate(keySpec);
System.out.println("Algorithm: " + myPrivKey.getAlgorithm());
You mentioned that you may not know what algorithm the key is using. I'm sure there is a more elegant solution than this, but you could create several KeyFactory objects (one for each possible algorithm) and try to generatePrivate() on each one until you do not get an InvalidKeySpecException.
thanks #gtrig using ur idea and editing the code like this :
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(KeyBytes);
try
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
privateKey = keyFactory.generatePrivate(keySpec);
algorithm = keyFactory.getAlgorithm();
//algorithm = "RSA";
//publicKey = keyFactory.generatePublic(keySpec);
} catch (InvalidKeySpecException excep1) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
privateKey = keyFactory.generatePrivate(keySpec);
algorithm = keyFactory.getAlgorithm();
//publicKey = keyFactory.generatePublic(keySpec);
} catch (InvalidKeySpecException excep2) {
KeyFactory keyFactory = KeyFactory.getInstance("EC");
privateKey = keyFactory.generatePrivate(keySpec);
} // inner catch
}
the code is working well now
This code generates a pair of public/private keys:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair keypair = keyGen.genKeyPair();
PrivateKey privateKey = keypair.getPrivate();
PublicKey publicKey = keypair.getPublic();
What I'd like to know is how do you usually store the public key:
Option 1: store the bytes
byte[] privateKeyBytes = privateKey.getEncoded();
byte[] publicKeyBytes = publicKey.getEncoded();
// ... write to file
// convert bytes back to public/private keys
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
What I don't like is to tie the code to concrete implementations such as PKCS8EncodedKeySpec and X509EncodedKeySpec.
Option 2: store the modulus and exponent
KeyFactory fact = KeyFactory.getInstance("RSA");
RSAPublicKeySpec pub = fact.getKeySpec(publicKey, RSAPublicKeySpec.class);
RSAPrivateKeySpec priv = fact.getKeySpec(privateKey,RSAPrivateKeySpec.class);
// store modulus and exponent as BigIntegers
BigInteger modulus = pub.getModulus());
BigInteger exponent = pub.getPublicExponent());
// ... write to file
// recreate public key (the same applies to the private key)
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(keySpec);
The second option is easier to implement, but I don't know if it could be less performant.
Any advise ?
In our applications, we store public and private keys in DER format so they can be used and manipulated outside java more easily. In our case, the private keys do not have passwords on them.
To convert the private key to something more easily usable in java:
openssl pkcs8 -topk8 -nocrypt -in key.pem -inform PEM -out key.der -outform DER
Then you can obtain an RSA private key directly by:
public static RSAPrivateKey getPrivateKey(File privateKeyFile) throws IOException, GeneralSecurityException {
byte[] keyBytes = new byte[(int)privateKeyFile.length()];
FileInputStream fis = new FileInputStream(privateKeyFile);
fis.read(keyBytes);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPrivateKey privKey = (RSAPrivateKey) keyFactory.generatePrivate(spec);
return privKey;
}
The public key is similar:
openssl rsa -in private.pem -pubout -outform DER -out public.der
and to read it:
public static RSAPublicKey getPublicKey(File publicKeyFile) throws IOException, GeneralSecurityException {
byte[] keyBytes = new byte[(int)publicKeyFile.length()];
FileInputStream fis = new FileInputStream(publicKeyFile);
fis.read(keyBytes);
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
RSAPublicKey pubKey = (RSAPublicKey)factory.generatePublic(publicKeySpec);
return pubKey;
}
Many people store then keystores. For our purposes, we needed the same key to be shared across multiple applications in several different languages, and didn't want to duplicate the files on disk.
In either case, the performance shouldn't be a huge concern, because you're likely to store those keys in some sort of Singleton or cache instead of regenerating them each time.
You're actually storing the bytes in both cases whether you realize it or not. I suppose the correct answer is hinted at in #Brian M. Carr answer, which is to store the higher-level object in its most natural form. In the case of public keys, the obvious choices are as a PKCS#1 RSAPublicKey ASN.1 structure, DER-encoded, or as an X509 SubjectPublicKeyInfo ASN.1 structure, DER-encoded. The latter is what the Sun providers give you, which the sun class X509EncodedKeySpec supports. Similarly, the PKCS8EncodedKeySpec supports a private key format. Both these formats are standards, and are supported by openssl for example. Sun tends -- tended :( -- to support existing standards rather then define their own.
If you want to define a format for storing the keys, then I would choose a format that is expendable so that it doesn't break when you want to change encryption (when the old one gets to weak for example).
So i would store the bytes encoded as base64, together with a string that describes the format, "rsa" maybe.