Java 使用PEM私钥对字符串进行签名
我有一个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
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());
}
}