Использование закодированного PEM-шифрованного закрытого ключа для подписи сообщения изначально
Я пытаюсь использовать сертификат PEM (X.509) (хранящийся в файле privateKey.pem на диске) для подписывания сообщений, отправленных через сокеты в Java, но мне очень трудно найти пример, который закрывается. Обычно я парень из С++, который просто вступает, чтобы помочь в этом проекте, поэтому мне было сложно скомпоновать все вместе в код, который работает, когда я незнаком с API.
К сожалению, меня ограничивают методы, которые поставляются с Java (1.6.0 Update 16), поэтому, хотя я нашел аналогичный пример, используя BouncyCastle PEMReader, это не помогло в этом конкретном проекте.
Мой ключ privateKey.pem защищен парольной фразой в виде:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED DEK-Info:
DES-EDE3-CBC,63A862F284B1280B
[...]
tsjQI4H8lhOBuk+NelHu7h2+uqbBQzwkPoA8IqbPXUz+B/MAGhoTGl4AKPjfm9gu
OqEorRU2vGiSaUPgDaRhdPKK0stxMxbByUi8xQ2156d/Ipk2IPLSEZDXONrB/4O5
[...]
-----END RSA PRIVATE KEY-----
Они были сгенерированы с использованием OpenSSL:
openssl.exe genrsa -out private_key.pem 4096
Я не могу преобразовать этот ключ в DER или другой формат до начала выполнения, любые необходимые преобразования необходимо будет сделать внутренне в коде, так как ключ должен быть легко заменен, а формат останется PEM.
Я слышал смесь вещей, о которых я не совсем уверен, и надеялся, что коллективные умы здесь, в SO, помогут собрать кусочки вместе.
Я слышал, что он сказал, что для сертификата PEM требуется Base64 Decoded, чтобы преобразовать его в сертификат DER, который можно использовать. У меня есть инструмент для декодирования Base64, называемый MiGBase64, но я не совсем уверен, как/когда это декодирование необходимо выполнить.
Я потерялся в документах API Java, пытаясь отслеживать 15 различных типов ключей, KeyStores, KeyGenerators, Certificates и т.д., которые существуют, но я недостаточно знаком с любым из них, чтобы правильно определите, что мне нужно использовать, и как их использовать вместе.
Базовый алгоритм кажется довольно простым, поэтому особенно неприятно, что я не смог написать такую же простую реализацию:
1) Прочтите файл privateKey.pem из файла
2) Загрузите закрытый ключ в класс XXX, используя Passphrase для дешифрования ключа
3) Используйте ключевой объект с классом Signature для подписания сообщения
Помощь с этим, особенно пример кода, очень ценится. Я пытаюсь найти полезные примеры для этой проблемы, так как большинство "закрытых" примеров генерируют новые ключи, используя BouncyCastle, или просто иначе используют разные формы ключей/классов, которые здесь не применимы.
Это кажется действительно простой проблемой, но это сводит меня с ума, любые действительно простые ответы?
Ответы
Ответ 1
Если вы используете BouncyCastle, попробуйте следующее:
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.security.KeyPair;
import java.security.Security;
import java.security.Signature;
import java.util.Arrays;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.openssl.PasswordFinder;
import org.bouncycastle.util.encoders.Hex;
public class SignatureExample {
public static void main(String [] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
String message = "hello world";
File privateKey = new File("private.pem");
KeyPair keyPair = readKeyPair(privateKey, "password".toCharArray());
Signature signature = Signature.getInstance("SHA256WithRSAEncryption");
signature.initSign(keyPair.getPrivate());
signature.update(message.getBytes());
byte [] signatureBytes = signature.sign();
System.out.println(new String(Hex.encode(signatureBytes)));
Signature verifier = Signature.getInstance("SHA256WithRSAEncryption");
verifier.initVerify(keyPair.getPublic());
verifier.update(message.getBytes());
if (verifier.verify(signatureBytes)) {
System.out.println("Signature is valid");
} else {
System.out.println("Signature is invalid");
}
}
private static KeyPair readKeyPair(File privateKey, char [] keyPassword) throws IOException {
FileReader fileReader = new FileReader(privateKey);
PEMReader r = new PEMReader(fileReader, new DefaultPasswordFinder(keyPassword));
try {
return (KeyPair) r.readObject();
} catch (IOException ex) {
throw new IOException("The private key could not be decrypted", ex);
} finally {
r.close();
fileReader.close();
}
}
private static class DefaultPasswordFinder implements PasswordFinder {
private final char [] password;
private DefaultPasswordFinder(char [] password) {
this.password = password;
}
@Override
public char[] getPassword() {
return Arrays.copyOf(password, password.length);
}
}
}
Ответ 2
Команда OpenSSL создает пару ключей и кодирует ее в формате PKCS # 1. Если вы не используете шифрование (не указали пароль для команды), PEM является просто DER с кодировкой Base64 для PKCS # 1 RSAPrivateKey.
К сожалению, Sun JCE не предоставляет публичный интерфейс для чтения ключа в этом формате. У вас есть 2 варианта,
-
Импортируйте ключ в хранилище ключей, и вы можете прочитать его оттуда. Keytool не позволяет импортировать личные ключи. Вы можете найти другие инструменты для этого.
-
В библиотеке OAuth есть функция для обработки этого. Посмотрите на код здесь,
http://oauth.googlecode.com/svn/code/java/core/commons/src/main/java/net/oauth/signature/pem/PKCS1EncodedKeySpec.java