Java asymmetric encryption: the preferred way to store public / private keys
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 want to know is how you usually store public keys:
Option 1: store 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 binding code to specific implementations, such as pkcs8encoded keyspec and x509encoded keyspec
Option 2: store modulus and index
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 may have poor performance
Any suggestions?
Solution
In our application, we store public and private keys in der format for easier use and operation outside Java In our example, the private key has no password
Convert the private key to something easier to use in Java:
openssl pkcs8 -topk8 -nocrypt -in key.pem -inform PEM -out key.der -outform DER
You can then obtain the 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; }
Public keys are similar:
openssl rsa -in private.pem -pubout -outform DER -out public.der
And 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 then store the keystore For our purposes, we need to share the same key among multiple applications in several different languages, and we don't want to copy files on disk
In either case, performance should not be a huge problem, because you may store these keys in some kind of singleton or cache instead of regenerating each time