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

StackOverflow https://stackoverflow.com/questions/3441501

Вопрос

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

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

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

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

Другие советы

Вы на самом деле храняте байты в обоих случаях, понимаете ли вы это или нет. Я полагаю, что правильный ответ намекается на @bris M. Carre ответ, который должен хранить объект более высокого уровня в его наиболее естественной форме. В случае публичных ключей очевидным вариантом является как PKCS # 1 RSAPUBLABLEYKEY ASN.1 Структура, Der-encoded или в качестве X509 CublicPublicKeyInfo Структура ASN.1, DER-encoded. Последнее - то, что дает вам поставщики Солнца, которые поддерживают Sun Class X509Encodedkeyspec. Точно так же PKCS8ENCODEDKEYSPEC поддерживает формат закрытого ключа. Оба эти форматы являются стандартами и поддерживаются OpenSSL, например. Солнце стремится - как правило :( - Чтобы поддержать существующие стандарты, а затем определить свои собственные.

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

Поэтому я бы хранил байты, закодированные как Base64, вместе со строкой, которая описывает формат «RSA».

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top