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

Java 使用PEM编码、加密的私钥对消息进行本机签名,java,signature,pem,Java,Signature,Pem,我试图使用PEM(X.509)证书(存储在磁盘上的privateKey.PEM文件中)对通过Java套接字发送的消息进行签名,但在找到一个接近的示例时遇到了很多困难。我通常是一个C++的家伙,他刚刚介入帮助这个项目,所以我很难把它全部放到我不熟悉API的代码中。 不幸的是,我仅限于Java标准的方法(1.6.0更新16),所以尽管我发现了一个使用的类似示例,但它对这个特定项目没有多大帮助 My privateKey.pem密钥受密码保护,形式如下: -----BEGIN RSA PRIVATE

我试图使用PEM(X.509)证书(存储在磁盘上的privateKey.PEM文件中)对通过Java套接字发送的消息进行签名,但在找到一个接近的示例时遇到了很多困难。我通常是一个C++的家伙,他刚刚介入帮助这个项目,所以我很难把它全部放到我不熟悉API的代码中。 不幸的是,我仅限于Java标准的方法(1.6.0更新16),所以尽管我发现了一个使用的类似示例,但它对这个特定项目没有多大帮助

My 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解码才能转换为可以使用的DER证书。我有一个名为Base64的解码工具,但不完全确定该解码需要如何/何时完成


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


基本算法似乎非常简单,这就是为什么我没有能够编写一个同样简单的实现特别令人沮丧的原因:

1) 从文件中读取privateKey.pem
2) 将私钥加载到XXX类中,使用密码短语解密密钥
3) 使用带有签名类的密钥对象对消息进行签名



非常感谢您的帮助,尤其是示例代码。我一直在努力寻找解决这个问题的有用示例,因为大多数“接近”的示例都是使用BouncyCastle生成新的键,或者只是使用不同形式的键/类,这些键/类在这里不适用


这似乎是一个非常简单的问题,但它让我抓狂,有任何非常简单的答案吗?

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

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

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

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

  • OAuth库有一个函数来处理这个问题。看看这里的代码


  • 感谢您在回答中花费的时间和精力。我不得不做一些修改,让它与我的钥匙一起工作,但你带头。非常感谢@凯文克:很高兴听到。哪些修改是必要的?有用的信息,感谢您花时间回答:)我选择了BouncyCastle,因为它似乎适合我的特殊用途,但我感谢您的帮助。谢谢