Шифроване с RSA частен ключ в Java

Опитвам се да криптирам някакво съдържание с RSA частен ключ.

Следвам този пример: http://www.junkheap.net/content/public_key_encryption_java

но го преобразува да използва частни ключове, а не публични. Следвайки този пример, мисля, че това, което трябва да направя, е:

  • Прочетете в частен ключ във формат DER
  • Генерирайте PCKS8EncodedKeySpec
  • извикайте generatePrivate() от KeyFactory, за да получите частен ключов обект
  • Използвайте този частен ключ с обекта Cipher, за да извършите криптирането

И така, стъпките:

Ключът е генериран от openssl с:

openssl genrsa -aes256 -out private.pem 2048

и след това беше преобразуван във формат DER с:

openssl rsa -in private.pem -outform DER -out private.der

Генерирам PKCS8EncodedKeySpec с:

byte[] encodedKey = new byte[(int)inputKeyFile.length()];

try {
    new FileInputStream(inputKeyFile).read(encodedKey);
} catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedKey);
return privateKeySpec;

И след това генерирайте частния ключов обект с:

PrivateKey pk = null;

try {
    KeyFactory kf = KeyFactory.getInstance(RSA_METHOD);
    pk = kf.generatePrivate(privateKeySpec);
} catch (NoSuchAlgorithmException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (InvalidKeySpecException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
return pk;

Въпреки това, при обаждане до:

pk = kf.generatePrivate(privateKeySpec);

Взимам:

java.security.spec.InvalidKeySpecException: Unknown key spec.
at com.sun.net.ssl.internal.ssl.JS_KeyFactory.engineGeneratePrivate(DashoA12275)
at com.sun.net.ssl.internal.ssl.JSA_RSAKeyFactory.engineGeneratePrivate(DashoA12275)
at java.security.KeyFactory.generatePrivate(KeyFactory.java:237)

Въпроси:

  • Правилен ли е общият подход?
  • Дали PCKS8EncodedKeySpec е правилната ключова спецификация за използване?
  • Някакви мисли относно грешката в спецификацията на невалидния ключ?

person wadesworld    schedule 08.09.2009    source източник
comment
Прегледах този и ги генерирах с Java: stackoverflow.com/questions/19640735/   -  person Hayro    schedule 27.02.2014


Отговори (5)


Първо, объркан съм защо планирате да използвате Cipher за криптиране с частен ключ, вместо да подписвате с Signature. Не съм сигурен, че всички доставчици на RSA Cipher ще използват правилния тип блок за настройка, но си струва да опитате.

Като оставим това настрана обаче, мисля, че се опитвате да заредите нестандартен ключ във формат OpenSSL. Преобразуването му в DER с rsa е по същество просто декодиране на base-64; структурата на ключа не е PKCS #8.

Вместо това, след genrsa, използвайте командата openssl pkcs8, за да конвертирате генерирания ключ в некриптиран PKCS #8, DER формат:

openssl pkcs8 -topk8 -nocrypt -in private.pem -outform der -out private.der

Това ще създаде некриптиран частен ключ, който може да бъде зареден с PKCS8EncodedKeySpec.

person erickson    schedule 08.09.2009
comment
Честно казано, не знаех за Signature. За да съм сигурен, че разбирам употребата му, бих инициализирал обекта за подпис, бих извикал update с байтовете, които искам да подпиша, и след това call sign? И тогава мога да запазя байтовете, върнати от sign, като мой цифров подпис? - person wadesworld; 08.09.2009
comment
Здравейте. Някой най-накрая разреши ли проблема? Имам privateKey, който не мога да заредя в java, за да продължа с фазата на подписване. Моят privateKey е RSA, PKCS#8 DER и има парола. Как мога да го заредя в java? Изключението в моя случай е java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : DER input, Integer tag error - person BRabbit27; 10.12.2011
comment
@BRabbit27 За да заредите директно личен ключ, той трябва да е некриптиран. Използвайте командата openssl pkcs8, която показвам по-горе, като добавите опцията -inform der; ще ви подкани за паролата. Ако искате да запазите личния си ключ криптиран (което силно препоръчвам), ще трябва да го добавите към хранилище за ключове (като PKCS #12 файл) и да получите достъп до него чрез KeyStore API в Java. - person erickson; 10.12.2011
comment
@erickson Опитах командата, но тя показва грешка (използвам WinOpenSSL) грешка при декриптиране на ключ. Командата, която използвах беше openssl pkcs8 -topk8 -nocrypt -in myKey.key -inform der -outform der -out myNoCryptKey.key - person BRabbit27; 12.12.2011
comment
АКТУАЛИЗАЦИЯ Какво трябва да прави опцията -nocrypt? Мисля, че направих заобиколно решение като това openssl pkcs8 -inform DER -in myKey.key -outform PEM -out myKeyInPEM.key попита ме за паролата ми и след това openssl pkcs8 -topk8 -nocrypt -in myKeyInPEM -outform DER -out myKeyInDERNoCrypted.key не попита за паролата ми, но резултатният файл изглежда равен на myKey.key - person BRabbit27; 12.12.2011
comment
@BRabbit27 Опцията -nocrypt означава, че изходният файл няма да бъде шифрован с парола. Файлът изглежда равен или файлът е равен? Можете да изчислите хеша на двата файла с помощта на OpenSSL, нали? - person erickson; 12.12.2011
comment
@erickson изглежда равен. Всъщност изчислих хеша и той е различен. Най-накрая успях да го заредя от java. Единствените оставащи въпроси са защо трябва да премина от DER-Encrypted към PEM към DER-NoCrypt? За всички ключове, които имам, трябва ли да ги конвертирам в DER-NoCrypt, за да мога да ги заредя от java? Много благодаря! - person BRabbit27; 12.12.2011
comment
Не съм сигурен защо трябваше да използвате междинния PEM файл. Вероятно не бих могъл да ви кажа, без всъщност да опитам сам с един от вашите ключове. Когато изпробвах процеса с моите ключове, не трябваше да преминавам през тази стъпка. - person erickson; 12.12.2011

Не можете да шифровате с частен ключ. Ако JCE ви позволява да направите това, то е просто случайно.

Трябва да използвате подпис. Ето кодовия фрагмент за това,

signer = Signature.getInstance("SHA1withRSA");
signer.initSign(privateKey); // PKCS#8 is preferred
signer.update(dataToSign);
byte[] signature = signer.sign();
person ZZ Coder    schedule 08.09.2009
comment
s/public/private/ в първия ви ред. - person caf; 08.09.2009

Не е случайно, че криптирането с частен ключ е разрешено. Ако искате да разделите подпис на индивидуално хеширане и криптиране, криптирането с частен ключ е от съществено значение. Да кажем, че имам документ, който трябва да подпиша и ключът ми се намира на мрежов HSM. Сега или предавам целия документ към HSM за подписване, или мога да създам локален хеш и да го предавам към HSM само за криптиране. Моят избор ще зависи от това дали локалното хеш изчисление ми дава по-добра производителност, а именно делегирано хеш изчисление с мрежово забавяне.

person Kapil    schedule 22.04.2010

Този въпрос е доста стар, но наскоро се натъкнах на проблема (изпълнявам изисквания на някакъв протокол, който изисква криптиране с частен ключ). Просто ще цитирам публикацията от форум:

Наскоро се натъкнах на същия проблем, подадох PMR 22265,49R и поддръжката на IBM след консултация с разработката (които и да са те) реши, че частните ключове не могат да се използват за криптиране. Колкото и да се опитвах да споря с тях, че частните ключове не трябва да се използват за защита на данните, което е само една от целите зад криптирането, и че е напълно добре да се използват частни ключове за криптиране, за да се постигне неотричане, те бяха непоклатими в тяхната вяра. Трябва да обичаш хората, които настояват, че 2x2=5.

Ето как заобиколих този проблем: По същество създадох обект с публичен ключ с крипто материал на частния ключ. Ще трябва да направите обратното, да създадете частен ключов обект с крипто материал на публичния ключ, за да декриптирате с публичен ключ, ако искате да избегнете Публичния ключ не може да се използва за декриптиране на изключение.

RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) ks.getKey(keyAlias, ksPassword.trim().toCharArray());
RSAPublicKeySpec spec = new RSAPublicKeySpec(
   privateKey.getModulus(),
   privateKey.getPrivateExponent()
);
Key fakePublicKey = KeyFactory.getInstance("RSA").generatePublic(spec);
encryptCipher.init(Cipher.ENCRYPT_MODE, fakePublicKey);
person dmitry    schedule 08.02.2014
comment
Това е много рисковано, ако третирате експонента на частния ключ като публичен експонент и го разпространите, тъй като даден частен ключ (който наричате публичен ключ) е лесно да се извлече действителният публичен ключ (който сега наричате частен ключ). Не пускайте случайно този публичен ключ публично, или вашата система ще бъде компрометирана. Може да е толкова просто, колкото да познаете, че показателят на частния ключ е 65537. - person Jim Flood; 24.05.2014
comment
Търсих това толкова дълго. Толкова съм благодарен за този отговор. Името частен и публичен ключ е напълно погрешно в този случай на употреба, но има ситуации, в които това е уместно. - person BluBb_mADe; 30.06.2015
comment
@JimFlood Ако разбирам правилно и моята ситуация е подобна, dmitry създаде RSAPublicKeySpec за съхранение на данните за частния ключ. Очевидно той няма да разпространи частния ключ, а само оригиналния публичен ключ, който сега може да се използва за доказване (дешифриране), че частният ключ е бил за криптиране - т.е. неотричане. - person Guss; 24.07.2017

Опитайте тази:

java.security.Security.addProvider(
                     new org.bouncycastle.jce.provider.BouncyCastleProvider()
            );
person Jimmy Chan    schedule 06.05.2015