使用编码的PEM,加密私钥对本地注册的消息(Using a PEM encoded, encrypt

2019-08-19 08:21发布

I'm trying to use a PEM(X.509) certificate (stored in a privateKey.pem file on disk) to sign messages sent via sockets in Java, but am having a lot of trouble finding an example that's close. I'm normally a C++ guy who's just stepping in to help on this project, so it's been a little difficult for me to put it all together into code that works when I'm unfamiliar with the APIs.

Unfortunately, I'm limited to methods that come standard with Java (1.6.0 Update 16), so although I found a similar example using BouncyCastle's PEMReader, it hasn't helped much on this particular project.

My privateKey.pem key is passphrase protected, in the form of:

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

They key was generated using OpenSSL:

openssl.exe genrsa -out private_key.pem 4096

I am unable to convert this key to a DER or other format prior to runtime, any conversions necessary will need to be done internally in code, as the key needs to be easily replaceable and the format will remain PEM.

I've heard a mix of things which I'm not entirely sure about, and was hoping the collective minds here at SO could help pull the pieces together.



I've heard it said that the PEM certificate needs Base64 Decoded to convert it into a DER certificate that can be used. I have a Base64 Decoding tool called MiGBase64 but am not entirely sure how/when this decoding needs done.


I've gotten lost in the Java API docs trying to track down the 15 different types of Keys, KeyStores, KeyGenerators, Certificates, etc that exist, but I'm not familiar enough with any of them to properly identify which I need to be using, and how to use them together.


The basic algorithm seems pretty simple, which is why it has been particularly frustrating that I haven't been able to write an equally simple implementation:

1) Read the privateKey.pem from file
2) Load the private Key into XXX class, using the Passphrase to decrypt the key
3) Use the key object with the Signature class to sign the message



Help with this, especially example code, is greatly appreciated. I've been struggling to find useful examples for this problem, as most 'close' examples are generating new keys, using BouncyCastle, or just otherwise using different forms of keys/classes not applicable here.

This seems like a really simple problem but it's driving me crazy, any really simple answers?

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


Answer 2:

OpenSSL命令产生密钥对和在PKCS#1格式编码它。 如果不使用加密(没有提供命令密码),质子交换膜是根本的PKCS#1 RSAPrivateKey Base64编码DER。

不幸的是,Sun的JCE没有提供一个公共接口来读取这种格式的关键。 你有2种选择,

  1. 导入钥匙插入密钥库,你可以从那里阅读。 密钥工具不允许进口私钥。 你可以找到其他工具来做到这一点。

  2. OAuth的库有一个函数来处理这个问题。 看看代码在这里,

http://oauth.googlecode.com/svn/code/java/core/commons/src/main/java/net/oauth/signature/pem/PKCS1EncodedKeySpec.java



文章来源: Using a PEM encoded, encrypted private key to sign a message natively