Java асимметричное шифрование: предпочтительный способ сохранить общественность/закрытые ключи

Этот код генерирует пару общественности/закрытых ключей:

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);

Вторую опцию легче реализовать, но я не знаю, могло ли это быть менее производительно.

Кто-либо советует?

12
задан Guido 9 August 2010 в 15:18
поделиться

3 ответа

В наших приложениях мы храним открытые и закрытые ключи в формате 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;
}

Многие люди затем хранят хранилища ключей. Для наших целей нам нужен был общий ключ для нескольких приложений на нескольких разных языках, и мы не хотели дублировать файлы на диске.

В любом случае производительность не должна вызывать большого беспокойства, потому что вы, вероятно, будете хранить эти ключи в каком-то синглтоне или кеше вместо того, чтобы каждый раз восстанавливать их.

13
ответ дан 2 December 2019 в 20:15
поделиться

Вы фактически сохраняете байты в обоих случаях, понимаете вы это или нет. Я полагаю, что на правильный ответ намекает ответ @Brian M. Carr, который должен хранить объект более высокого уровня в его наиболее естественной форме. В случае открытых ключей очевидным выбором является структура PKCS # 1 RSAPublicKey ASN.1 с кодировкой DER или структура X509 SubjectPublicKeyInfo ASN.1 с кодировкой DER. Последнее - это то, что вам предлагают поставщики Sun, которые поддерживает класс Sun X509EncodedKeySpec. Точно так же PKCS8EncodedKeySpec поддерживает формат закрытого ключа. Оба эти формата являются стандартами и поддерживаются, например, openssl. Sun стремится - стремится :( - поддерживать существующие стандарты, а не определять свои собственные.

2
ответ дан 2 December 2019 в 20:15
поделиться

Если вы хотите определить формат для хранения ключей, то я бы выбрал формат, который можно использовать, чтобы он не сломался, когда вы захотите сменить шифрование (например, когда старое станет слабым).

Поэтому я бы хранил байты, закодированные в base64, вместе со строкой, описывающей формат, например, "rsa".

1
ответ дан 2 December 2019 в 20:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: