使用 PEM 编码、加密的私钥对消息进行本地签名

发布于 2024-08-08 12:03:06 字数 1628 浏览 12 评论 0 原文

我正在尝试使用 PEM(X.509) 证书(存储在磁盘上的 privateKey.pem 文件中)对通过 Java 套接字发送的消息进行签名,但在找到接近的示例时遇到了很多麻烦。我通常是一个 C++ 人员,只是介入来帮助这个项目,所以当我不熟悉 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证书。我有一个名为 MiGBase64 的 Base64 解码工具,但我不完全确定如何/何时需要完成此解码。


我在 Java API 文档中迷失了方向,试图追踪现有的 15 种不同类型的密钥、密钥库、密钥生成器、证书等,但我对其中任何一个都不够熟悉,无法正确识别我需要的密钥。需要使用,以及如何一起使用它们。


基本算法看起来非常简单,这就是为什么我无法编写一个同样简单的实现特别令人沮丧的原因:

1)从文件中读取 privateKey.pem
2) 将私钥加载到XXX类中,使用Passphrase解密密钥
3) 使用Key对象和Signature类对消息进行签名



非常感谢对此的帮助,特别是示例代码。我一直在努力寻找解决此问题的有用示例,因为大多数“接近”示例都使用 BouncyCastle 生成新密钥,或者只是使用此处不适用的不同形式的密钥/类。

这似乎是一个非常简单的问题,但它让我发疯,有什么非常简单的答案吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

挽梦忆笙歌 2024-08-15 12:03:06

如果您使用的是 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);
        }
    } 
}

If you're using BouncyCastle, try the following:

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);
        }
    } 
}
君勿笑 2024-08-15 12:03:06

OpenSSL 命令生成密钥对并以 PKCS#1 格式对其进行编码。如果您不使用加密(未提供命令密码),则 PEM 只是 PKCS#1 RSAPrivateKey 的 Base64 编码 DER。

不幸的是,Sun 的 JCE 没有提供公共接口来读取这种格式的密钥。您有 2 个选项,

  1. 将密钥导入密钥库,您可以从那里读取它。 Keytool 不允许导入私钥。您可以找到其他工具来执行此操作。

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

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

The OpenSSL command generate key-pair and encodes it in PKCS#1 format. If you don't use the encryption (didn't provide password for the command), the PEM is simply Base64-encoded DER for the PKCS#1 RSAPrivateKey.

Unfortunately, Sun's JCE doesn't provide a public interface to read the key in this format. You have 2 options,

  1. Import the key into keystore and you can read it from there. Keytool doesn't allow for importing of private keys. You can find other tools to do this.

  2. OAuth library has a function to handle this. Look at code here,

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文