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

Использование закодированного 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, или просто иначе используют разные формы ключей/классов, которые здесь не применимы.

Это кажется действительно простой проблемой, но это сводит меня с ума, любые действительно простые ответы?

4b9b3361

Ответ 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