Java 使用PEM私钥对字符串进行签名

Java 使用PEM私钥对字符串进行签名,java,android,rsa,Java,Android,Rsa,我有一个PEM编码的私钥,我需要用它对字符串签名。但代码不断崩溃,只有一个例外: java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0c0890ba:ASN.1 encoding routines:asn1_check_tlen:WRONG_TAG 关键字符串: -----BEGIN ENCRYPTED PRIVATE KEY----- MIICxjBABgkqhkiG9w0BBQ0wMz

我有一个PEM编码的私钥,我需要用它对字符串签名。但代码不断崩溃,只有一个例外:

java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0c0890ba:ASN.1 encoding routines:asn1_check_tlen:WRONG_TAG
关键字符串:

-----BEGIN ENCRYPTED PRIVATE KEY-----
MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI4P/+9mJV6RwCAggA 
MBQGCCqGSIb3DQMHBAg/ZWGXeLHgeASCAoAhExhFxfcikmIKbFP0rgAlJuj1r999 
... and so on...
hlgzM2p71FdC6NDVyyxbit/IzbimtJyhkRwOAnZ98yqtXWUEOx2v7CcUqiU8dSLA 
K0PsaxNTUeUcQV+Z7yJk/8HxfE1ya3u2CgPXCZsWWmbxQG/+awE0eEnZ 
-----END ENCRYPTED PRIVATE KEY-----
我尝试了许多变体,查看了许多答案,但结果都是一样的

编辑:在James K Polk的帮助下,我成功地获得了私钥字节,但现在我得到了
java.security.nosuchalgorithException:SecretKeyFactory PBES2实现未找到。
修改后的代码:

private String sign(String dataString, String pkString, String privateKeyPass) throws Exception {
        pkString = pkString.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "");
        pkString = pkString.replace("-----END ENCRYPTED PRIVATE KEY-----", "");
        pkString = pkString.replaceAll("\\s+","");
        byte[] privateKeyBytes = decryptPrivateKey(Base64.decode(pkString, Base64.DEFAULT), privateKeyPass);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));

        Signature instance = Signature.getInstance("SHA1withRSA");
        instance.initSign(privateKey);
        instance.update(dataString.getBytes(UTF_8));
        return Base64.encodeToString(instance.sign(), Base64.DEFAULT);
    }

public static byte[] decryptPrivateKey(byte[] key, String pass) throws Exception {
        PBEKeySpec passKeySpec = new PBEKeySpec(pass.toCharArray());

        EncryptedPrivateKeyInfo encryptedKey = new EncryptedPrivateKeyInfo(key);
        Timber.w("encryptedKey.getAlgName(): %s", encryptedKey.getAlgName());
        SecretKeyFactory keyFac = SecretKeyFactory.getInstance(encryptedKey.getAlgName());//PBES2
        SecretKey passKey = keyFac.generateSecret(passKeySpec);

        // Create PBE Cipher
        Cipher pbeCipher = Cipher.getInstance(encryptedKey.getAlgName());
        // Initialize PBE Cipher with key and parameters
        pbeCipher.init(Cipher.DECRYPT_MODE, passKey, encryptedKey.getAlgParameters());

        // Decrypt the private key
        return pbeCipher.doFinal(encryptedKey.getEncryptedData());
    }
编辑:我最终使用的类来自:


我将密钥串保存到一个文件中,然后将其馈送到readPrivateKeyFile

您的私钥是根据PKCS#8加密的,因此您需要使用该类。包含一个显示如何检索它的示例。我已将其转化为以下更完整的示例:

import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.List;

public class Main {

    private static byte [] pemFileToBytes(String filename) throws IOException {
        // read in PEM file, throw away the begin and end lines
        List<String> pemLines = Files.readAllLines(Paths.get(filename), StandardCharsets.US_ASCII);
        pemLines.remove(0);
        pemLines.remove(pemLines.size() - 1);
        String pem = String.join("", pemLines);

        // base64 decode and return the result.

        return Base64.getDecoder().decode(pem);
    }

    private static PrivateKey parsePrivateKey (String filename, char [] password) throws Exception{
        PBEKeySpec passKeySpec = new PBEKeySpec(password); //my password

        EncryptedPrivateKeyInfo encryptedKey = new EncryptedPrivateKeyInfo(pemFileToBytes(filename));
        SecretKeyFactory keyFac = SecretKeyFactory.getInstance(encryptedKey.getAlgName());
        SecretKey passKey = keyFac.generateSecret(passKeySpec);

        // Create PBE Cipher
        Cipher pbeCipher = Cipher.getInstance(encryptedKey.getAlgName());
        // Initialize PBE Cipher with key and parameters
        pbeCipher.init(Cipher.DECRYPT_MODE, passKey, encryptedKey.getAlgParameters());

        // Decrypt the private key

        byte [] encodedPrivateKey = pbeCipher.doFinal(encryptedKey.getEncryptedData());
        PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePrivate(privateKeySpec);

    }

    public static void main(String[] args) throws Exception {
        PrivateKey pk = parsePrivateKey("x.pk8", "pass".toCharArray());
    }
}
导入javax.crypto.Cipher;
导入javax.crypto.EncryptedPrivateKeyInfo;
导入javax.crypto.SecretKey;
导入javax.crypto.SecretKeyFactory;
导入javax.crypto.spec.PBEKeySpec;
导入java.io.IOException;
导入java.nio.charset.StandardCharset;
导入java.nio.file.Files;
导入java.nio.file.path;
导入java.security.KeyFactory;
导入java.security.PrivateKey;
导入java.security.spec.PKCS8EncodedKeySpec;
导入java.util.Base64;
导入java.util.List;
公共班机{
私有静态字节[]pemFileToBytes(字符串文件名)引发IOException{
//读入PEM文件,扔掉开头和结尾行
List pemLines=Files.readAllLines(path.get(filename)、StandardCharsets.US\u ASCII);
pemLines。移除(0);
pemLines.remove(pemLines.size()-1);
String pem=String.join(“,pemLines);
//base64解码并返回结果。
返回Base64.getDecoder().decode(pem);
}
私有静态PrivateKey parsePrivateKey(字符串文件名,char[]密码)引发异常{
PBEKeySpec passKeySpec=新的PBEKeySpec(密码);//我的密码
EncryptedPrivateKeyInfo encryptedKey=新的EncryptedPrivateKeyInfo(pemFileToBytes(文件名));
SecretKeyFactory keyFac=SecretKeyFactory.getInstance(encryptedKey.getAlgName());
SecretKey passKey=keyFac.generateSecret(passKeySpec);
//创建PBE密码
Cipher pbeCipher=Cipher.getInstance(encryptedKey.getAlgName());
//使用密钥和参数初始化PBE密码
pbeCipher.init(Cipher.DECRYPT_模式,passKey,encryptedKey.getAlgParameters());
//解密私钥
字节[]encodedPrivateKey=pbeCipher.doFinal(encryptedKey.getEncryptedData());
PKCS8EncodedKeySpec privateKeySpec=新的PKCS8EncodedKeySpec(encodedPrivateKey);
KeyFactory kf=KeyFactory.getInstance(“RSA”);
返回kf.generatePrivate(privateKeySpec);
}
公共静态void main(字符串[]args)引发异常{
PrivateKey pk=parsePrivateKey(“x.pk8”,“pass.tocharray());
}
}

最后一行,
返回新字符串(instance.sign(),UTF_8)
没有任何意义,因为
Signature.sign()
返回的字节数组在任何字符集中都不可能是有效字符串。如果必须将签名转换为字符串,则标准方法是对其进行base64编码。

无论我将哪个字节[]馈送到此函数,我都会得到“ASN1异常:错误的内容长度”:整个pem字符串,没有标头,没有标头base64解码。我应该从pem字符串中得到什么字节[]?@ildarishalin:我已经编辑了我的答案,以显示一个更完整的示例。谢谢,我会将你的答案标记为已接受。但是,我仍然在“SecretKeyFactory.getInstance(encryptedKey.getAlgName())”中得到一个NoSuchAlgorithmException。我尝试过一些android支持的算法,但没有成功。我想,我将不得不尝试使用openssl@ildarishalin:我将在Android上进一步研究它,我在Java 8 SE上进行了测试。到目前为止,我得到的是:我使用
String algorithm=“pbewithsha1和c2”代替
encryptedKey.getAlgName()
(未设置,我从SecretKeyFactory android文档获取)。这就引出了
无效算法参数异常:PBE要求设置PBE参数。
import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.List;

public class Main {

    private static byte [] pemFileToBytes(String filename) throws IOException {
        // read in PEM file, throw away the begin and end lines
        List<String> pemLines = Files.readAllLines(Paths.get(filename), StandardCharsets.US_ASCII);
        pemLines.remove(0);
        pemLines.remove(pemLines.size() - 1);
        String pem = String.join("", pemLines);

        // base64 decode and return the result.

        return Base64.getDecoder().decode(pem);
    }

    private static PrivateKey parsePrivateKey (String filename, char [] password) throws Exception{
        PBEKeySpec passKeySpec = new PBEKeySpec(password); //my password

        EncryptedPrivateKeyInfo encryptedKey = new EncryptedPrivateKeyInfo(pemFileToBytes(filename));
        SecretKeyFactory keyFac = SecretKeyFactory.getInstance(encryptedKey.getAlgName());
        SecretKey passKey = keyFac.generateSecret(passKeySpec);

        // Create PBE Cipher
        Cipher pbeCipher = Cipher.getInstance(encryptedKey.getAlgName());
        // Initialize PBE Cipher with key and parameters
        pbeCipher.init(Cipher.DECRYPT_MODE, passKey, encryptedKey.getAlgParameters());

        // Decrypt the private key

        byte [] encodedPrivateKey = pbeCipher.doFinal(encryptedKey.getEncryptedData());
        PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePrivate(privateKeySpec);

    }

    public static void main(String[] args) throws Exception {
        PrivateKey pk = parsePrivateKey("x.pk8", "pass".toCharArray());
    }
}