Подтвердить что ты не робот

Асимметричное шифрование 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);

Второй вариант проще реализовать, но я не знаю, может ли он быть менее результативным.

Любые советы?

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

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

Итак, я бы сохранил байты, закодированные как base64, вместе со строкой, которая описывает формат "rsa", возможно.