Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java asymmetric encryption: 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'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 ?

like image 600
Guido Avatar asked Aug 09 '10 15:08

Guido


People also ask

Is public private key encryption asymmetric?

Public key cryptography, also known as asymmetric cryptography, uses two separate keys instead of one shared one: a public key and a private key. Public key cryptography is an important technology for Internet security.

Why it is secure to share public keys openly in an asymmetric encryption system?

The primary advantage of asymmetric encryption is that there is no need to securely transmit a secret key. Instead, the public key is published openly, made available to the entire world. There is no need to keep it secret, because it can't be used alone.

Do you encrypt with public or private key?

Public and private keys can also be used to create a digital signature. A digital signature assures that the person sending the message is who they claim to be. Typically, we use the recipient's public key to encrypt the data and the recipient then uses their private key to decrypt the data.


2 Answers

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.

like image 165
Brian M. Carr Avatar answered Oct 30 '22 11:10

Brian M. Carr


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.

like image 20
President James K. Polk Avatar answered Oct 30 '22 11:10

President James K. Polk