Sign string with PEM PrivateKey

匿名 (未验证) 提交于 2019-12-03 08:48:34

问题:

I have a PEM-encoded private key and I need to sign a string with it. But the code keeps crashing with exception:

java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0c0890ba:ASN.1 encoding routines:asn1_check_tlen:WRONG_TAG 

the key string:

-----BEGIN ENCRYPTED PRIVATE KEY----- MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI4P/+9mJV6RwCAggA  MBQGCCqGSIb3DQMHBAg/ZWGXeLHgeASCAoAhExhFxfcikmIKbFP0rgAlJuj1r999  ... and so on... hlgzM2p71FdC6NDVyyxbit/IzbimtJyhkRwOAnZ98yqtXWUEOx2v7CcUqiU8dSLA  K0PsaxNTUeUcQV+Z7yJk/8HxfE1ya3u2CgPXCZsWWmbxQG/+awE0eEnZ  -----END ENCRYPTED PRIVATE KEY----- 

I've tried many variants, looked through many answers, but the results were the same

Edit: with James K Polk's help I've managed to get private key bytes, but now I get java.security.NoSuchAlgorithmException: SecretKeyFactory PBES2 implementation not found. Modified code:

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

EDIT: I ended up using class from http://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art050:

PrivateKey privateKey = KeyImport.readPrivateKeyFile(pkFileName, privateKeyPass); 

I save keyString into a file and then feed it to readPrivateKeyFile

回答1:

Your private key is encrypted according PKCS#8, so you need to use the EncryptedPrivateKeyInfo class among others. This stackoverflow question contains an example showing how to retrieve it. I have turned it into a more complete example below:

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

You last line, return new String(instance.sign(), UTF_8); doesn't make any sense as Signature.sign() returns an array of bytes that is not likely to be a valid string in any character set. If you must convert the signature to a string then the standard way is to base64 encode it.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!