Этот код генерирует пару общественности/закрытых ключей:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair keypair = keyGen.genKeyPair();
PrivateKey privateKey = keypair.getPrivate();
PublicKey publicKey = keypair.getPublic();
То, что я хотел бы знать, - то, как делают Вас, обычно хранят открытый ключ:
Опция 1: сохраните байты
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);
То, что я не люблю, должно связать код с конкретными реализациями такой как PKCS8EncodedKeySpec
и X509EncodedKeySpec
.
Опция 2: сохраните модуль и экспоненту
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);
Вторую опцию легче реализовать, но я не знаю, могло ли это быть менее производительно.
Кто-либо советует?
В наших приложениях мы храним открытые и закрытые ключи в формате DER, чтобы их было проще использовать и управлять ими вне java. В нашем случае на закрытых ключах нет паролей.
Чтобы преобразовать закрытый ключ во что-то более удобное для использования в java:
openssl pkcs8 -topk8 -nocrypt -in key.pem -inform PEM -out key.der -outform DER
Затем вы можете получить закрытый ключ RSA напрямую:
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;
}
Открытый ключ аналогичен:
openssl rsa -in private.pem -pubout -outform DER -out public.der
и прочитать его:
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;
}
Многие люди затем хранят хранилища ключей. Для наших целей нам нужен был общий ключ для нескольких приложений на нескольких разных языках, и мы не хотели дублировать файлы на диске.
В любом случае производительность не должна вызывать большого беспокойства, потому что вы, вероятно, будете хранить эти ключи в каком-то синглтоне или кеше вместо того, чтобы каждый раз восстанавливать их.
Вы фактически сохраняете байты в обоих случаях, понимаете вы это или нет. Я полагаю, что на правильный ответ намекает ответ @Brian M. Carr, который должен хранить объект более высокого уровня в его наиболее естественной форме. В случае открытых ключей очевидным выбором является структура PKCS # 1 RSAPublicKey ASN.1 с кодировкой DER или структура X509 SubjectPublicKeyInfo ASN.1 с кодировкой DER. Последнее - это то, что вам предлагают поставщики Sun, которые поддерживает класс Sun X509EncodedKeySpec. Точно так же PKCS8EncodedKeySpec поддерживает формат закрытого ключа. Оба эти формата являются стандартами и поддерживаются, например, openssl. Sun стремится - стремится :( - поддерживать существующие стандарты, а не определять свои собственные.
Если вы хотите определить формат для хранения ключей, то я бы выбрал формат, который можно использовать, чтобы он не сломался, когда вы захотите сменить шифрование (например, когда старое станет слабым).
Поэтому я бы хранил байты, закодированные в base64, вместе со строкой, описывающей формат, например, "rsa".