我在弄清楚如何正确读取pem文件的私钥方面遇到了困难。我已经在stackoverflow上浏览了不同的主题,但是我找不到解决方案。我想实现的是从类路径中读取pkcs#8编码文件中的加密私钥,并将其作为内存密钥存储库中的密钥条目加载。下面是我试图解析的一个示例私钥,密码是秘密
。它纯粹是为了共享而创建的,所以它不是生产机器上使用的私钥。
我使用以下命令从p12文件创建了它:openssl pkcs12-in key pair。p12-输出密钥对。pem
有效示例(一次性)密钥对
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI9/FSBonUacYCAggA
MBQGCCqGSIb3DQMHBAidGkS8wYhOpwSCBMi8JaSYOKudMNNFRpzL7QMIZgFtzDay
MmOroy3lW34dOa7dusqDl4d2gklKcHCpbEaTYxm5aQJ1LuiOdGtFy7HwxOvKU5xz
4qsJoeBIpE0eCTKjQW7/I38DzLXx2wUURqhMWOtFsWZEyR5Dqok3N9mIKKKBXAFG
AwNjlTRW2LyPSttiIUGN01lthjifMWoLTWB1aSGOmGeJRBdSZeqZ15xKneR4H5ja
yE88YcpOHCDKMIxi6ZVoKs7jDQhu8bBKqS8NsYyh1AlP9QkvWNal36jWSzhqYNzk
NRWUOZngfkdbMALVfRtbrC215jHGWVwosPIIs8rkoarRv8s6QWS1Rg3YfQ3qgcRf
s7hkDFKJf3TUXr+askfamV5hc300ZG64+ldX1YxWXY8Vd/wIvHAc/YE/lTyCgYrY
19Am6MNBfp8/kXvzKj+PizB8oNDO4S8sSShEEzOQ5a/+MTC6bqB0DLWYGUqRbjLc
PyYTC2C4i9Agx/GeGVE3c1UdtXiwwnt2XUn7Y1YGqABk0xGIY4J1NFTbSOxKl9hO
arwopAFrZU5nsjjFzv1DJvhfQWnYX18kPSKNHDlia019M118qZ8ERwD9tH8ix9Fa
R2tQdxn1aRGmvXSw+zFkbWD8aWs9n/B+QN1yllJqVoWypOld1yj+fVYYnYOtV1gK
eiygrtrh3JJCvLbEQl4nOgJM3PlEtfBHSaunehIXQMD1z/NDUqgBYjuDPyqRxJeH
Va5k72Nds5PeySKJJnICB3nZKjqgfLhNUrXa1SAQ4vqr0Ik/Lu9P7T+B1XiYwuUT
a20+bxi/x89ZZqwp3jnDuHup7XcO1MtqsoOKP/JgkjVMesb8Q1W8i2dXzg+l4gkk
l1ipreEGtT1YfFTq0DFelz6CjZFLDlGGeGWob94sW94DWTW0nsLPhQWEnwW1CcyJ
oJbJdDEgdiIbRJoABDkTuVXLwTlgzHSHh6zeJvNvcojI7UI3nWYCVYvD3kwghXiP
67sKGL3ug7PFDqLia46AudGY7CFh4+wpxyH+fidLC3FMdkDBA6xR6mGgEjRLXR9M
TnJ/eSYP7eqYZeKn9EarcI7v1zM2IG0/PDQCetiI0ABiHpdKyRQuuiEavp3xC5Vi
h7UmJNYt8Zsz3rwqAQ4FR2+Su5R34OOdRmxTaYLe96PXTpLcLef5TkYixSY7Tzgd
PMyRxRPrywklUEFe4KK/KOcdolxybfsIsxQnupLAMEsO7/Cs7mouNHISK51haDRc
vNbKQ5E4xOq1U4ThW5dHR29cGZillfmMzj05ZQh3ZX2TQJP45ahFET3v9kInWCwQ
8atqclVPOSnASsJZ0PxjYgKZuY8QWYM6zpfWyWnfu/CHhWbRS/qX8T1ow2SMyPBL
CQbZ+MhcdP0IrjoXhDFQsns16i/BPK5TTVqtEC2ywDf5P4/BOEZkySG9YNOd6THp
VA/dVPafzmLy3ltqH+jG8ZH2+RtWx7kwBjiDWs5cF33BFrPS7AZlzMzZoCHLXD/r
T/SmisybUKHMqri0x0RHeIByW0hogSByWiyIn8POabDzJV6Df9nQPziDGcSsvWfG
7q+hizh6+nnXOY+GZx3ptwg9mA9R4QyCiFNQradOaXSPxyEL2IC77/srFfVEIaU4
SRo=
-----END ENCRYPTED PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDTjCCAjagAwIBAgIEVnHI3TANBgkqhkiG9w0BAQsFADBIMQswCQYDVQQGEwJO
TDEVMBMGA1UEChMMVGh1bmRlcmJlcnJ5MRIwEAYDVQQLEwlBbXN0ZXJkYW0xDjAM
BgNVBAMTBUhha2FuMB4XDTIwMDgzMTA4MDczOVoXDTMwMDgyOTA4MDczOVowSDEL
MAkGA1UEBhMCTkwxFTATBgNVBAoTDFRodW5kZXJiZXJyeTESMBAGA1UECxMJQW1z
dGVyZGFtMQ4wDAYDVQQDEwVIYWthbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAJZ+pqirEqEk4k1ow8vryld79oCO4P/1y+v60CkLpS2MpeHE3BogTr7g
WWP5HdHBMU5l8yT5tXuyVZZqgyzo0q2Sdm7GGrNunHf6Z8vPQFu69sQC1utDM9u8
ZFKiKsTNJ+5QS6KsOtlACyhaLoaPAWVjtvueMjVwmM9hfv/Gq6VyyuBm2x1C4HTj
zPCLHE2G1D13EJaWsvyZLGSbl0GGXZGPhaDd/vnw5TW36mvNTWW+37ZIEk4kXANa
FUNsJemm8HjB/PzHs3/SXGxuD8NKobg3+cNXYwAz2s2DI0W6Xw2g5bbrMQAdBRvn
9/kNftMymDORw3RGwDM2ld4zQfIkNrkCAwEAAaNAMD4wHQYDVR0OBBYEFHhT7ATg
oVVFIsglxD/1iUBRB6gDMB0GA1UdEQEB/wQTMBGCCWxvY2FsaG9zdIcEfwAAATAN
BgkqhkiG9w0BAQsFAAOCAQEAhVWH/CgZ0ZNmTfiFAOnWdaZVaa7vAFPT2YbXuvlY
YIRlru0B/zn2Mfwmn5W2o1CqoBuhyfErkkF4aRM1vduIirUjlcH4+cFXrV2gtlnf
eWTg/sJJmYzkJTGeOIqRlB1HKCqoeNCrykkcsikECQ1nCqr1qLh9DXsUgWVW57YW
qvP1P8xOO2/J9shMB6lOhftpawrqZ2hNG8fqMKjVVuUpFBNR+WODQ/rRRtqa6uU2
V8aOOZx1QJUkTdN76YOCuGET7edevjpdbRXde+HQN6mbT9OLxSZHO0aQrDyDmNhp
aVHuQn/KtYNWCZ78XKK8wtVnflmfqE/c9xO1n/EcVvLCdg==
-----END CERTIFICATE-----
我知道BouncyCastle能够解析它,但是我想避免使用额外的库。所以我想知道是否可以只使用普通的jdk或其他轻量级库。
我已经能够用以下页眉/页脚解析私钥:
-----BEGIN PRIVATE KEY-----
*
-----END PRIVATE KEY-----
和
-----BEGIN RSA PRIVATE KEY-----
*
-----END RSA PRIVATE KEY-----
我已经使用了以下代码片段来实现这一点:
import com.hierynomus.asn1.ASN1InputStream;
import com.hierynomus.asn1.encodingrules.der.DERDecoder;
import com.hierynomus.asn1.types.ASN1Object;
import com.hierynomus.asn1.types.constructed.ASN1Sequence;
import com.hierynomus.asn1.types.primitive.ASN1Integer;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class App {
private static final String KEYSTORE_TYPE = "PKCS12";
private static final String KEY_FACTORY_ALGORITHM = "RSA";
private static final String CERTIFICATE_TYPE = "X.509";
private static final Pattern CERTIFICATE_PATTERN = Pattern.compile("-----BEGIN CERTIFICATE-----(.*?)-----END CERTIFICATE-----", Pattern.DOTALL);
private static final Pattern PRIVATE_KEY_PATTERN = Pattern.compile("-----BEGIN PRIVATE KEY-----(.*?)-----END PRIVATE KEY-----", Pattern.DOTALL);
private static final Pattern ENCRYPTED_PRIVATE_KEY_PATTERN = Pattern.compile("-----BEGIN ENCRYPTED PRIVATE KEY-----(.*?)-----END ENCRYPTED PRIVATE KEY-----", Pattern.DOTALL);
private static final Pattern RSA_PRIVATE_KEY_PATTERN = Pattern.compile("-----BEGIN RSA PRIVATE KEY-----(.*?)-----END RSA PRIVATE KEY-----", Pattern.DOTALL);
private static final String NEW_LINE = "\n";
private static final String EMPTY = "";
public static void main(String[] args) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, InvalidKeySpecException {
String privateKeyContent = "";
String certificateContent = "";
PrivateKey privateKey = parsePrivateKey(privateKeyContent);
Certificate[] certificates = parseCertificate(certificateContent).values()
.toArray(new Certificate[]{});
KeyStore keyStore = createEmptyKeyStore();
keyStore.setKeyEntry("client", privateKey, null, certificates);
}
private static KeyStore createEmptyKeyStore() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
keyStore.load(null, null);
return keyStore;
}
private static PrivateKey parsePrivateKey(String identityContent) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
KeySpec keySpec = null;
Matcher privateKeyMatcher = PRIVATE_KEY_PATTERN.matcher(identityContent);
if (privateKeyMatcher.find()) {
String privateKeyContent = privateKeyMatcher.group(1).replace(NEW_LINE, EMPTY).trim();
byte[] decodedPrivateKeyContent = Base64.getDecoder().decode(privateKeyContent);
keySpec = new PKCS8EncodedKeySpec(decodedPrivateKeyContent);
}
privateKeyMatcher = RSA_PRIVATE_KEY_PATTERN.matcher(identityContent);
if (privateKeyMatcher.find()) {
String privateKeyContent = privateKeyMatcher.group(1).replace(NEW_LINE, EMPTY).trim();
byte[] decodedPrivateKeyContent = Base64.getDecoder().decode(privateKeyContent);
keySpec = getKeySpecFromAsn1StructuredData(decodedPrivateKeyContent);
}
privateKeyMatcher = ENCRYPTED_PRIVATE_KEY_PATTERN.matcher(identityContent);
if (privateKeyMatcher.find()) {
String privateKeyContent = privateKeyMatcher.group(1).replace(NEW_LINE, EMPTY).trim();
byte[] decodedPrivateKeyContent = Base64.getDecoder().decode(privateKeyContent);
keySpec = null; //TODO
}
Objects.requireNonNull(keySpec);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
return keyFactory.generatePrivate(keySpec);
}
private static KeySpec getKeySpecFromAsn1StructuredData(byte[] decodedPrivateKeyContent) throws IOException {
try(ByteArrayInputStream privateKeyAsInputStream = new ByteArrayInputStream(decodedPrivateKeyContent)) {
ASN1InputStream stream = new ASN1InputStream(new DERDecoder(), privateKeyAsInputStream);
ASN1Sequence asn1Sequence = stream.readObject();
if (asn1Sequence.getValue().size() < 9) {
throw new RuntimeException("Parsed key content doesn't have the minimum required sequence size of 9");
}
BigInteger modulus = extractIntValueFrom(asn1Sequence.get(1));
BigInteger publicExponent = extractIntValueFrom(asn1Sequence.get(2));
BigInteger privateExponent = extractIntValueFrom(asn1Sequence.get(3));
BigInteger primeP = extractIntValueFrom(asn1Sequence.get(4));
BigInteger primeQ = extractIntValueFrom(asn1Sequence.get(5));
BigInteger primeExponentP = extractIntValueFrom(asn1Sequence.get(6));
BigInteger primeExponentQ = extractIntValueFrom(asn1Sequence.get(7));
BigInteger crtCoefficient = extractIntValueFrom(asn1Sequence.get(8));
return new RSAPrivateCrtKeySpec(
modulus, publicExponent, privateExponent,
primeP, primeQ, primeExponentP, primeExponentQ, crtCoefficient
);
}
}
private static BigInteger extractIntValueFrom(ASN1Object<?> asn1Object) {
if (asn1Object instanceof ASN1Integer) {
return ((ASN1Integer) asn1Object).getValue();
} else {
throw new RuntimeException(String.format(
"Unable to parse the provided value of the object type [%s]. The type should be an instance of [%s]",
asn1Object.getClass().getName(), ASN1Integer.class.getName())
);
}
}
private static Map<String, Certificate> parseCertificate(String certificateContent) throws IOException, CertificateException {
Map<String, Certificate> certificates = new HashMap<>();
Matcher certificateMatcher = CERTIFICATE_PATTERN.matcher(certificateContent);
while (certificateMatcher.find()) {
String sanitizedCertificate = certificateMatcher.group(1).replace(NEW_LINE, EMPTY).trim();
byte[] decodedCertificate = Base64.getDecoder().decode(sanitizedCertificate);
try(ByteArrayInputStream certificateAsInputStream = new ByteArrayInputStream(decodedCertificate)) {
CertificateFactory certificateFactory = CertificateFactory.getInstance(CERTIFICATE_TYPE);
Certificate certificate = certificateFactory.generateCertificate(certificateAsInputStream);
certificates.put(UUID.randomUUID().toString(), certificate);
}
}
return certificates;
}
}
我还需要处理asn。1编码的私钥。我可以用BouncyCastle,但我还是想避开它,因为它很重。我从这个话题中得到启发:https://stackoverflow.com/a/42733858/6777695但从jdk 11开始,DerInputStream和DerValue就不再可访问了。应该避免阳光照射。。。所以我发现了asn一个java库来解析asn。1个加密的私钥,它工作得很好。
我已经分享了我处理其他案件的全部片段,所以当有人想帮助我时,尝试会更容易。
从以下文章:DER和PEM中的ASN.1密钥结构我了解到具有以下页眉/页脚的私钥:-----BEGIN ENCRYPTED PRIVATE KEY----- * -----END ENCRYPTED PRIVATE KEY-----
也是具有以下结构的asn.1编码私钥:
EncryptedPrivateKeyInfo ::= SEQUENCE {
encryptionAlgorithm EncryptionAlgorithmIdentifier,
encryptedData EncryptedData
}
EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
EncryptedData ::= OCTET STRING
我也尝试了这里提供的答案如何用java读取密码加密密钥?但是这些也不起作用。然而,我不太确定如何正确解析它到java对象并将其作为KeySpec加载。所以欢迎任何帮助!
======
Matcher privateKeyMatcher = Pattern.compile("-----BEGIN ENCRYPTED PRIVATE KEY-----(.*?)-----END ENCRYPTED PRIVATE KEY-----", Pattern.DOTALL)
.matcher(identityContent);
if (privateKeyMatcher.find()) {
String privateKeyContent = privateKeyMatcher.group(1).replace(NEW_LINE, EMPTY).trim();
byte[] decodedPrivateKeyContent = Base64.getDecoder().decode(privateKeyContent);
try (ByteArrayInputStream privateKeyAsInputStream = new ByteArrayInputStream(decodedPrivateKeyContent)) {
ASN1InputStream stream = new ASN1InputStream(new DERDecoder(), privateKeyAsInputStream);
ASN1Sequence asn1Sequence = stream.readObject();
ASN1OctetString privateKeyAsOctetString = (ASN1OctetString) asn1Sequence.get(1);
ASN1OctetString saltAsOctetString = (ASN1OctetString) ((ASN1Sequence) ((ASN1Sequence) ((ASN1Sequence) ((ASN1Sequence) asn1Sequence.get(0)).get(1)).get(0)).get(1)).get(0);
ASN1OctetString initializationVectorAsOctec = ((ASN1OctetString) ((ASN1Sequence) ((ASN1Sequence) ((ASN1Sequence) asn1Sequence.get(0)).get(1)).get(1)).get(1));
ASN1Integer iterationCount = (ASN1Integer) ((ASN1Sequence) ((ASN1Sequence) ((ASN1Sequence) ((ASN1Sequence) asn1Sequence.get(0)).get(1)).get(0)).get(1)).get(1);
IvParameterSpec ivParameterSpec = new IvParameterSpec(initializationVectorAsOctec.getValue());
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2withHmacSHA1");
EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(secretKeyFactory.getAlgorithm(), privateKeyAsOctetString.getValue());
int keyLength = encryptedPrivateKeyInfo.getEncoded().length;
PBEKeySpec pbeKeySpec = new PBEKeySpec(keyPassword, saltAsOctetString.getValue(), iterationCount.getValue().intValue(), keyLength);
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding/");
SecretKey secretKey = secretKeyFactory.generateSecret(pbeKeySpec);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
PKCS8EncodedKeySpec keySpec = encryptedPrivateKeyInfo.getKeySpec(cipher);
}
}
结果进入以下异常:java.security.InvalidKeyExctive:错误的算法:DESede或TripleDES需要
对语句cipher.init(Cipher.DECRYPT_MODE,秘密密钥, ivParameterSpec);
======
Matcher privateKeyMatcher = Pattern.compile("-----BEGIN ENCRYPTED PRIVATE KEY-----(.*?)-----END ENCRYPTED PRIVATE KEY-----", Pattern.DOTALL)
.matcher(identityContent);
if (privateKeyMatcher.find()) {
String privateKeyContent = privateKeyMatcher.group(1).replace(NEW_LINE, EMPTY).trim();
byte[] decodedPrivateKeyContent = Base64.getDecoder().decode(privateKeyContent);
try (ByteArrayInputStream privateKeyAsInputStream = new ByteArrayInputStream(decodedPrivateKeyContent)) {
ASN1InputStream stream = new ASN1InputStream(new DERDecoder(), privateKeyAsInputStream);
ASN1Sequence asn1Sequence = stream.readObject();
ASN1OctetString privateKeyAsOctetString = (ASN1OctetString) asn1Sequence.get(1);
ASN1OctetString saltAsOctetString = (ASN1OctetString) ((ASN1Sequence) ((ASN1Sequence) ((ASN1Sequence) ((ASN1Sequence) asn1Sequence.get(0)).get(1)).get(0)).get(1)).get(0);
ASN1OctetString initializationVectorAsOctec = ((ASN1OctetString) ((ASN1Sequence) ((ASN1Sequence) ((ASN1Sequence) asn1Sequence.get(0)).get(1)).get(1)).get(1));
ASN1Integer iterationCount = (ASN1Integer) ((ASN1Sequence) ((ASN1Sequence) ((ASN1Sequence) ((ASN1Sequence) asn1Sequence.get(0)).get(1)).get(0)).get(1)).get(1);
IvParameterSpec ivParameterSpec = new IvParameterSpec(initializationVectorAsOctec.getValue());
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2withHmacSHA1");
EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(secretKeyFactory.getAlgorithm(), privateKeyAsOctetString.getValue());
int keyLength = 24 * 8;
PBEKeySpec pbeKeySpec = new PBEKeySpec(keyPassword, saltAsOctetString.getValue(), iterationCount.getValue().intValue(), keyLength);
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding/");
SecretKey secretKeyPbkdf = secretKeyFactory.generateSecret(pbeKeySpec);
SecretKey secretKey = new SecretKeySpec(secretKeyPbkdf.getEncoded(), "DESede");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
PKCS8EncodedKeySpec keySpec = encryptedPrivateKeyInfo.getKeySpec(cipher);
}
}
如果你不羞于在使用openssl之前转换给定的加密私钥,你可以使用示例代码来轻松解析。
为了运行我的示例,我将您的加密私钥保存到文件so_privatekey_secret.pem。
使用openssl中的这条命令行,我用另一个密码转换了(已经)加密的密钥,并生成了(新)密钥文件“so_privatekey_3des_secret”。佩姆:
openssl pkcs8 -v1 PBE-SHA1-3DES -topk8 -in so_privatekey_secret.pem -out so_privatekey_3des_secret.pem
您的加密私钥将如下所示:
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIE6jAcBgoqhkiG9w0BDAEDMA4ECAdT0CV4sPOPAgIIAASCBMi3H3WjyWEiAS/G
gw6gZTHiZFmidehU8XPh1HPYVb385u36WO16CwZVIBperQoXptCaE2LRwxmrN7T/
K2l59EHlV8i8FAfGKqvc7b1DW1fqx7jvw+/iT//3uNc3GHUNXfBrwHDi/MTghII4
gEFNUm7ZO7rAqeR7pJnZxm3giCvRHFbFcZRkz6LhCif3eYF9Lx3gSe2x6SPeNG/T
p0vrierxhHyCJk+S543J3cZ4vHUOjYzOsT1xz9hTbxAxeAEPkaCpD0P9jyth8M8k
2SRedRvR1Ia6r7sAfFz6XoHz5jH/lhd47EYpOc/A7rsgWkFLObwdFwGLRIvQsgeN
VXnwg5UoxRdjE3ZXQMFgHgzc4igmoVCjwgNZW6nAMwlcTctjsQyvRnAEnQbqLUw1
kddkjDzC1LFk+NiZ7L9oKVWAQuAQnLHMOxr8kJrTlSoS8zt5x5OEnR4I7JnUEHqs
2RythkJnoAsXaslBIkIXw4Kg6wisZjr/fL7ER0D0TBdZFPMvdMw8p4TxNxEF7v/K
G97HmCe39cVl3DkYayK2Bs4T6FxqbqUzUMOQnNHpbj82+ybEhm/5duhD5Kd/Inz7
8mTpDM08P7OMG27AiJDReQJ18XQ5y3DxQJu8UgfQEtjpVW3BQIVzYRM4JqEEv72h
YLELhtmBQom1SWadMW6jzRJRS+f+ivEApVyLcKlnPj2SY7niSmDjYChDx30fHoDt
z25UYBb8LmZ6P5rwNpMgGutBYqg721ioYt86mQsDDaLYOxjlhPfwbrQ9HqUrPCyQ
0UBeP9hNYkGbZQ+erZmyJ4hhlGGXz9v1LZ4xqNelKuUUREZaLWNVO7UwX2AgUebv
V7V3ne3ZNuLo5kDQqGz3hxwI4tcHgneNMpbxwdR7/B9QTXtirgZck9QIU6zxPhIf
agyCfpqE5pE3ln5n+FfbOik1pNcQxFr0nLy6vnlcTATLHftX0JycFc9OcJ9eRnI/
zk0ekYcw7QLxzNp/ews+Krwy+2SXaacTi+fXihCks10FZ/TEMYV0wC7OMW1yC35K
jUU12u7kSccvUk4xQftyA39XYCN6g7kxDqLjCfc6pMMWZZ9anSo7rmuQ6scoMhdr
904PcX3ne9rBJLv3a3W25TolNKZx6JofCViyIipcJY4FRyIRoIfwUTZ643KLSZ4o
nAPxtWHpbgA8ucrMKwQ0kbhT/qEvz3HxliL91N7qqMfZ3RfsgK2fUXQZ4zsU1oZ2
f7UT9+D0w/Pusond7SHPFo9G68GjsE48CQ1J/l5QreZ+bLxBohs2xM1WIz2TBJX9
ghdbo+0eatoxfq0o/H0vj66xMd8fvqePrzNw3kSqSpdQiZl95vqWM8Y7xEW0PxAU
78I0koI98v/3n9abSUNDhodPsk8I3abr7MoQsKe7VSu/x+mgMcfvNOiH6j6LuUDx
Eii7nfHhC/M69hWoh8Y+a2qPdbT2WMZSgXdBC+dCQeL4o6+flht6/24Eg+HAQMyY
3X5aH2ZM4BJAyJvdtj7btMDKq6nunyMywYhIOgKTZUg8wytxDsi2F4KyNKpXXgsj
0yR2L6EUYeQ1ZLG/NcM86sHueF1x38RznpyD4rujRGslWAKRXk4D2O2GpYS4rYRk
4r4pPXltZ472q7PGBfM=
-----END ENCRYPTED PRIVATE KEY-----
运行我的示例程序(没有异常处理)会给出一个输出:
pkInfo.getAlgName: PBEWithSHA1AndDESede
decryptedPrivateKey: SunRsaSign RSA private CRT key, 2048 bits
params: null
modulus: 18998199686208613730227071552042680566660281525162377778227794768879762338654464000476266705180956908841709177555966832423085875590556580445428066261332223955361646420713402269532603328557674453787190302285379571808527818182992417461094551685532849252957724361115461909983939654809533488667032982867230996411860328826589806804115764031480737011900431983025490936659008858316889367140757195359233313370592669605576636360123189540118611655293825465309125578750878878980002346272931570343463549347548711194000065166346537274382220211242975801253306067862461345994215674963367480607539333468674983712264803816027753559737
private exponent: 2470690692670644289832084636740463653655882622624760758103597888447170520657826825702415751838767348735186037205082706146786714644526202528897926495648786865000645626802269367582073915029633022103975204027927584273373553784482725392082470421529769049284506228997163157587212073758970565083984956787424047216319298607371397767542633302071090323391531162434678662485530085857362259661656308472034152915312425224731842425134593468185574918070451135641322780271191791839285884885643517240131520070881951542294552185519522325857178404160441369354693465035929601010771762928436963178293113507661955562266846499005032897533
代码:
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
public class ReadPkcs8PrivateKeysSo {
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
String encrypted = new String(Files.readAllBytes(Paths.get("so_privatekey_3des_secret.pem")));
String password = "secret";
//Create object from encrypted private key
encrypted = encrypted.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "");
encrypted = encrypted.replace("-----END ENCRYPTED PRIVATE KEY-----", "");
EncryptedPrivateKeyInfo pkInfo = new EncryptedPrivateKeyInfo(Base64.getMimeDecoder().decode(encrypted));
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray()); // password
System.out.println("pkInfo.getAlgName: " + pkInfo.getAlgName());
SecretKeyFactory pbeKeyFactory = SecretKeyFactory.getInstance(pkInfo.getAlgName());
SecretKey secretKey = pbeKeyFactory.generateSecret(keySpec);
PKCS8EncodedKeySpec encodedKeySpec = pkInfo.getKeySpec(secretKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey decryptedPrivateKey = keyFactory.generatePrivate(encodedKeySpec);
System.out.println("decryptedPrivateKey: " + decryptedPrivateKey);
}
}
您编辑的代码给出了一个错误“java.security.InvalidKeyException:错误的密钥大小”,这是因为您使用以下行“读取”密钥长度:
int keyLength = encryptedPrivateKeyInfo.getEncoded().length;
对于给定的私钥,长度将是1247位,这是不允许的TripleDES长度。您可以将其修复为(硬编码):
int keyLength = 24 * 8;
现在我们得到了一个192位=24字节的密钥长度,这对于三重密钥来说是可以的,但是我们收到了一个新的错误:
java.security.InvalidKeyException: Wrong algorithm: DESede or TripleDES required
这种行为的原因是下面的行(注释掉),它可以通过另外2行得到修复:
// SecretKey secretKey = secretKeyFactory.generateSecret(pbeKeySpec); // ### old routine
SecretKey secretKeyPbkdf = secretKeyFactory.generateSecret(pbeKeySpec); // ### new
SecretKey secretKey = new SecretKeySpec(secretKeyPbkdf.getEncoded(), "DESede"); // ### new
现在您的代码已成功运行并打印出私钥和证书:
original keyLength: 1247
fixed keyLength(bit): 192
privateKey:
SunRsaSign RSA private CRT key, 2048 bits
params: null
modulus: 18998199686208613730227071552042680566660281525162377778227794768879762338654464000476266705180956908841709177555966832423085875590556580445428066261332223955361646420713402269532603328557674453787190302285379571808527818182992417461094551685532849252957724361115461909983939654809533488667032982867230996411860328826589806804115764031480737011900431983025490936659008858316889367140757195359233313370592669605576636360123189540118611655293825465309125578750878878980002346272931570343463549347548711194000065166346537274382220211242975801253306067862461345994215674963367480607539333468674983712264803816027753559737
private exponent: 2470690692670644289832084636740463653655882622624760758103597888447170520657826825702415751838767348735186037205082706146786714644526202528897926495648786865000645626802269367582073915029633022103975204027927584273373553784482725392082470421529769049284506228997163157587212073758970565083984956787424047216319298607371397767542633302071090323391531162434678662485530085857362259661656308472034152915312425224731842425134593468185574918070451135641322780271191791839285884885643517240131520070881951542294552185519522325857178404160441369354693465035929601010771762928436963178293113507661955562266846499005032897533
number of certificates: 1
certificate: 0 data:
[
[
Version: V3
Subject: CN=Hakan, OU=Amsterdam, O=Thunderberry, C=NL
Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
Key: Sun RSA public key, 2048 bits
params: null
modulus: 18998199686208613730227071552042680566660281525162377778227794768879762338654464000476266705180956908841709177555966832423085875590556580445428066261332223955361646420713402269532603328557674453787190302285379571808527818182992417461094551685532849252957724361115461909983939654809533488667032982867230996411860328826589806804115764031480737011900431983025490936659008858316889367140757195359233313370592669605576636360123189540118611655293825465309125578750878878980002346272931570343463549347548711194000065166346537274382220211242975801253306067862461345994215674963367480607539333468674983712264803816027753559737
public exponent: 65537
Validity: [From: Mon Aug 31 10:07:39 CEST 2020,
To: Thu Aug 29 10:07:39 CEST 2030]
Issuer: CN=Hakan, OU=Amsterdam, O=Thunderberry, C=NL
SerialNumber: [ 5671c8dd]
Certificate Extensions: 2
[1]: ObjectId: 2.5.29.17 Criticality=true
SubjectAlternativeName [
DNSName: localhost
IPAddress: 127.0.0.1
]
[2]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 78 53 EC 04 E0 A1 55 45 22 C8 25 C4 3F F5 89 40 xS....UE".%.?..@
0010: 51 07 A8 03 Q...
]
]
]
Algorithm: [SHA256withRSA]
Signature:
0000: 85 55 87 FC 28 19 D1 93 66 4D F8 85 00 E9 D6 75 .U..(...fM.....u
0010: A6 55 69 AE EF 00 53 D3 D9 86 D7 BA F9 58 60 84 .Ui...S......X`.
0020: 65 AE ED 01 FF 39 F6 31 FC 26 9F 95 B6 A3 50 AA e....9.1.&....P.
0030: A0 1B A1 C9 F1 2B 92 41 78 69 13 35 BD DB 88 8A .....+.Axi.5....
0040: B5 23 95 C1 F8 F9 C1 57 AD 5D A0 B6 59 DF 79 64 .#.....W.]..Y.yd
0050: E0 FE C2 49 99 8C E4 25 31 9E 38 8A 91 94 1D 47 ...I...%1.8....G
0060: 28 2A A8 78 D0 AB CA 49 1C B2 29 04 09 0D 67 0A (*.x...I..)...g.
0070: AA F5 A8 B8 7D 0D 7B 14 81 65 56 E7 B6 16 AA F3 .........eV.....
0080: F5 3F CC 4E 3B 6F C9 F6 C8 4C 07 A9 4E 85 FB 69 .?.N;o...L..N..i
0090: 6B 0A EA 67 68 4D 1B C7 EA 30 A8 D5 56 E5 29 14 k..ghM...0..V.).
00A0: 13 51 F9 63 83 43 FA D1 46 DA 9A EA E5 36 57 C6 .Q.c.C..F....6W.
00B0: 8E 39 9C 75 40 95 24 4D D3 7B E9 83 82 B8 61 13 .9.u@.$M......a.
00C0: ED E7 5E BE 3A 5D 6D 15 DD 7B E1 D0 37 A9 9B 4F ..^.:]m.....7..O
00D0: D3 8B C5 26 47 3B 46 90 AC 3C 83 98 D8 69 69 51 ...&G;F..<...iiQ
00E0: EE 42 7F CA B5 83 56 09 9E FC 5C A2 BC C2 D5 67 .B....V...\....g
00F0: 7E 59 9F A8 4F DC F7 13 B5 9F F1 1C 56 F2 C2 76 .Y..O.......V..v
]
下面是运行在OpenJava11上的完整代码:
import com.hierynomus.asn1.ASN1InputStream;
import com.hierynomus.asn1.encodingrules.der.DERDecoder;
import com.hierynomus.asn1.types.ASN1Object;
import com.hierynomus.asn1.types.constructed.ASN1Sequence;
import com.hierynomus.asn1.types.primitive.ASN1Integer;
import com.hierynomus.asn1.types.string.ASN1OctetString;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// https://mvnrepository.com/artifact/com.hierynomus/asn-one/0.4.0
// additionally you need https://mvnrepository.com/artifact/org.slf4j/slf4j-api/1.7.30
// and https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12/1.7.30
// https://mvnrepository.com/artifact/org.eclipse.equinox/org.apache.log4j/1.2.13.v200706111418
class MainOrg3 {
private static final String KEYSTORE_TYPE = "PKCS12";
private static final String KEY_FACTORY_ALGORITHM = "RSA";
private static final String CERTIFICATE_TYPE = "X.509";
private static final Pattern CERTIFICATE_PATTERN = Pattern.compile("-----BEGIN CERTIFICATE-----(.*?)-----END CERTIFICATE-----", Pattern.DOTALL);
private static final Pattern PRIVATE_KEY_PATTERN = Pattern.compile("-----BEGIN PRIVATE KEY-----(.*?)-----END PRIVATE KEY-----", Pattern.DOTALL);
private static final Pattern ENCRYPTED_PRIVATE_KEY_PATTERN = Pattern.compile("-----BEGIN ENCRYPTED PRIVATE KEY-----(.*?)-----END ENCRYPTED PRIVATE KEY-----", Pattern.DOTALL);
private static final Pattern RSA_PRIVATE_KEY_PATTERN = Pattern.compile("-----BEGIN RSA PRIVATE KEY-----(.*?)-----END RSA PRIVATE KEY-----", Pattern.DOTALL);
private static final String NEW_LINE = "\n";
private static final String EMPTY = "";
static char[] keyPassword = "secret".toCharArray();
public static void main(String[] args) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, InvalidKeySpecException {
String privateKeyContent = "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" +
"MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI9/FSBonUacYCAggA\n" +
"MBQGCCqGSIb3DQMHBAidGkS8wYhOpwSCBMi8JaSYOKudMNNFRpzL7QMIZgFtzDay\n" +
"MmOroy3lW34dOa7dusqDl4d2gklKcHCpbEaTYxm5aQJ1LuiOdGtFy7HwxOvKU5xz\n" +
"4qsJoeBIpE0eCTKjQW7/I38DzLXx2wUURqhMWOtFsWZEyR5Dqok3N9mIKKKBXAFG\n" +
"AwNjlTRW2LyPSttiIUGN01lthjifMWoLTWB1aSGOmGeJRBdSZeqZ15xKneR4H5ja\n" +
"yE88YcpOHCDKMIxi6ZVoKs7jDQhu8bBKqS8NsYyh1AlP9QkvWNal36jWSzhqYNzk\n" +
"NRWUOZngfkdbMALVfRtbrC215jHGWVwosPIIs8rkoarRv8s6QWS1Rg3YfQ3qgcRf\n" +
"s7hkDFKJf3TUXr+askfamV5hc300ZG64+ldX1YxWXY8Vd/wIvHAc/YE/lTyCgYrY\n" +
"19Am6MNBfp8/kXvzKj+PizB8oNDO4S8sSShEEzOQ5a/+MTC6bqB0DLWYGUqRbjLc\n" +
"PyYTC2C4i9Agx/GeGVE3c1UdtXiwwnt2XUn7Y1YGqABk0xGIY4J1NFTbSOxKl9hO\n" +
"arwopAFrZU5nsjjFzv1DJvhfQWnYX18kPSKNHDlia019M118qZ8ERwD9tH8ix9Fa\n" +
"R2tQdxn1aRGmvXSw+zFkbWD8aWs9n/B+QN1yllJqVoWypOld1yj+fVYYnYOtV1gK\n" +
"eiygrtrh3JJCvLbEQl4nOgJM3PlEtfBHSaunehIXQMD1z/NDUqgBYjuDPyqRxJeH\n" +
"Va5k72Nds5PeySKJJnICB3nZKjqgfLhNUrXa1SAQ4vqr0Ik/Lu9P7T+B1XiYwuUT\n" +
"a20+bxi/x89ZZqwp3jnDuHup7XcO1MtqsoOKP/JgkjVMesb8Q1W8i2dXzg+l4gkk\n" +
"l1ipreEGtT1YfFTq0DFelz6CjZFLDlGGeGWob94sW94DWTW0nsLPhQWEnwW1CcyJ\n" +
"oJbJdDEgdiIbRJoABDkTuVXLwTlgzHSHh6zeJvNvcojI7UI3nWYCVYvD3kwghXiP\n" +
"67sKGL3ug7PFDqLia46AudGY7CFh4+wpxyH+fidLC3FMdkDBA6xR6mGgEjRLXR9M\n" +
"TnJ/eSYP7eqYZeKn9EarcI7v1zM2IG0/PDQCetiI0ABiHpdKyRQuuiEavp3xC5Vi\n" +
"h7UmJNYt8Zsz3rwqAQ4FR2+Su5R34OOdRmxTaYLe96PXTpLcLef5TkYixSY7Tzgd\n" +
"PMyRxRPrywklUEFe4KK/KOcdolxybfsIsxQnupLAMEsO7/Cs7mouNHISK51haDRc\n" +
"vNbKQ5E4xOq1U4ThW5dHR29cGZillfmMzj05ZQh3ZX2TQJP45ahFET3v9kInWCwQ\n" +
"8atqclVPOSnASsJZ0PxjYgKZuY8QWYM6zpfWyWnfu/CHhWbRS/qX8T1ow2SMyPBL\n" +
"CQbZ+MhcdP0IrjoXhDFQsns16i/BPK5TTVqtEC2ywDf5P4/BOEZkySG9YNOd6THp\n" +
"VA/dVPafzmLy3ltqH+jG8ZH2+RtWx7kwBjiDWs5cF33BFrPS7AZlzMzZoCHLXD/r\n" +
"T/SmisybUKHMqri0x0RHeIByW0hogSByWiyIn8POabDzJV6Df9nQPziDGcSsvWfG\n" +
"7q+hizh6+nnXOY+GZx3ptwg9mA9R4QyCiFNQradOaXSPxyEL2IC77/srFfVEIaU4\n" +
"SRo=\n" +
"-----END ENCRYPTED PRIVATE KEY-----";
String certificateContent = "-----BEGIN CERTIFICATE-----\n" +
"MIIDTjCCAjagAwIBAgIEVnHI3TANBgkqhkiG9w0BAQsFADBIMQswCQYDVQQGEwJO\n" +
"TDEVMBMGA1UEChMMVGh1bmRlcmJlcnJ5MRIwEAYDVQQLEwlBbXN0ZXJkYW0xDjAM\n" +
"BgNVBAMTBUhha2FuMB4XDTIwMDgzMTA4MDczOVoXDTMwMDgyOTA4MDczOVowSDEL\n" +
"MAkGA1UEBhMCTkwxFTATBgNVBAoTDFRodW5kZXJiZXJyeTESMBAGA1UECxMJQW1z\n" +
"dGVyZGFtMQ4wDAYDVQQDEwVIYWthbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\n" +
"AQoCggEBAJZ+pqirEqEk4k1ow8vryld79oCO4P/1y+v60CkLpS2MpeHE3BogTr7g\n" +
"WWP5HdHBMU5l8yT5tXuyVZZqgyzo0q2Sdm7GGrNunHf6Z8vPQFu69sQC1utDM9u8\n" +
"ZFKiKsTNJ+5QS6KsOtlACyhaLoaPAWVjtvueMjVwmM9hfv/Gq6VyyuBm2x1C4HTj\n" +
"zPCLHE2G1D13EJaWsvyZLGSbl0GGXZGPhaDd/vnw5TW36mvNTWW+37ZIEk4kXANa\n" +
"FUNsJemm8HjB/PzHs3/SXGxuD8NKobg3+cNXYwAz2s2DI0W6Xw2g5bbrMQAdBRvn\n" +
"9/kNftMymDORw3RGwDM2ld4zQfIkNrkCAwEAAaNAMD4wHQYDVR0OBBYEFHhT7ATg\n" +
"oVVFIsglxD/1iUBRB6gDMB0GA1UdEQEB/wQTMBGCCWxvY2FsaG9zdIcEfwAAATAN\n" +
"BgkqhkiG9w0BAQsFAAOCAQEAhVWH/CgZ0ZNmTfiFAOnWdaZVaa7vAFPT2YbXuvlY\n" +
"YIRlru0B/zn2Mfwmn5W2o1CqoBuhyfErkkF4aRM1vduIirUjlcH4+cFXrV2gtlnf\n" +
"eWTg/sJJmYzkJTGeOIqRlB1HKCqoeNCrykkcsikECQ1nCqr1qLh9DXsUgWVW57YW\n" +
"qvP1P8xOO2/J9shMB6lOhftpawrqZ2hNG8fqMKjVVuUpFBNR+WODQ/rRRtqa6uU2\n" +
"V8aOOZx1QJUkTdN76YOCuGET7edevjpdbRXde+HQN6mbT9OLxSZHO0aQrDyDmNhp\n" +
"aVHuQn/KtYNWCZ78XKK8wtVnflmfqE/c9xO1n/EcVvLCdg==\n" +
"-----END CERTIFICATE-----";
PrivateKey privateKey = parsePrivateKey(privateKeyContent);
Certificate[] certificates = parseCertificate(certificateContent).values()
.toArray(new Certificate[]{});
KeyStore keyStore = createEmptyKeyStore();
keyStore.setKeyEntry("client", privateKey, null, certificates);
System.out.println("\nprivateKey:\n" + privateKey);
int certificatesLength = certificates.length;
System.out.println("\nnumber of certificates: " + certificatesLength);
for (int i = 0; i < certificatesLength; i++) {
System.out.println("\ncertificate: " + i + " data:\n" + certificates[i]);
}
}
private static KeyStore createEmptyKeyStore() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
keyStore.load(null, null);
return keyStore;
}
private static PrivateKey parsePrivateKey(String identityContent) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
KeySpec keySpec = null;
Matcher privateKeyMatcher = PRIVATE_KEY_PATTERN.matcher(identityContent);
if (privateKeyMatcher.find()) {
String privateKeyContent = privateKeyMatcher.group(1).replace(NEW_LINE, EMPTY).trim();
byte[] decodedPrivateKeyContent = Base64.getDecoder().decode(privateKeyContent);
keySpec = new PKCS8EncodedKeySpec(decodedPrivateKeyContent);
}
privateKeyMatcher = RSA_PRIVATE_KEY_PATTERN.matcher(identityContent);
if (privateKeyMatcher.find()) {
String privateKeyContent = privateKeyMatcher.group(1).replace(NEW_LINE, EMPTY).trim();
byte[] decodedPrivateKeyContent = Base64.getDecoder().decode(privateKeyContent);
keySpec = getKeySpecFromAsn1StructuredData(decodedPrivateKeyContent);
}
privateKeyMatcher = ENCRYPTED_PRIVATE_KEY_PATTERN.matcher(identityContent);
if (privateKeyMatcher.find()) {
String privateKeyContent = privateKeyMatcher.group(1).replace(NEW_LINE, EMPTY).trim();
byte[] decodedPrivateKeyContent = Base64.getDecoder().decode(privateKeyContent);
try (ByteArrayInputStream privateKeyAsInputStream = new ByteArrayInputStream(decodedPrivateKeyContent)) {
ASN1InputStream stream = new ASN1InputStream(new DERDecoder(), privateKeyAsInputStream);
ASN1Sequence asn1Sequence = stream.readObject();
ASN1OctetString privateKeyAsOctetString = (ASN1OctetString) asn1Sequence.get(1);
ASN1OctetString saltAsOctetString = (ASN1OctetString) ((ASN1Sequence) ((ASN1Sequence) ((ASN1Sequence) ((ASN1Sequence) asn1Sequence.get(0)).get(1)).get(0)).get(1)).get(0);
ASN1OctetString initializationVectorAsOctec = ((ASN1OctetString) ((ASN1Sequence) ((ASN1Sequence) ((ASN1Sequence) asn1Sequence.get(0)).get(1)).get(1)).get(1));
ASN1Integer iterationCount = (ASN1Integer) ((ASN1Sequence) ((ASN1Sequence) ((ASN1Sequence) ((ASN1Sequence) asn1Sequence.get(0)).get(1)).get(0)).get(1)).get(1);
IvParameterSpec ivParameterSpec = new IvParameterSpec(initializationVectorAsOctec.getValue());
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2withHmacSHA1");
EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(secretKeyFactory.getAlgorithm(), privateKeyAsOctetString.getValue());
int keyLength = encryptedPrivateKeyInfo.getEncoded().length;
System.out.println("original keyLength: " + keyLength);
keyLength = 24 * 8; // ### fixed
System.out.println("fixed keyLength(bit): " + keyLength);
PBEKeySpec pbeKeySpec = new PBEKeySpec(keyPassword, saltAsOctetString.getValue(), iterationCount.getValue().intValue(), keyLength);
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding/");
// SecretKey secretKey = secretKeyFactory.generateSecret(pbeKeySpec); // ### old routine
SecretKey secretKeyPbkdf = secretKeyFactory.generateSecret(pbeKeySpec); // ### new
SecretKey secretKey = new SecretKeySpec(secretKeyPbkdf.getEncoded(), "DESede"); // ### new
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
keySpec = encryptedPrivateKeyInfo.getKeySpec(cipher);
//PKCS8EncodedKeySpec keySpec = encryptedPrivateKeyInfo.getKeySpec(cipher);
} catch (NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
}
Objects.requireNonNull(keySpec);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
return keyFactory.generatePrivate(keySpec);
}
private static KeySpec getKeySpecFromAsn1StructuredData(byte[] decodedPrivateKeyContent) throws IOException {
try(ByteArrayInputStream privateKeyAsInputStream = new ByteArrayInputStream(decodedPrivateKeyContent)) {
ASN1InputStream stream = new ASN1InputStream(new DERDecoder(), privateKeyAsInputStream);
ASN1Sequence asn1Sequence = stream.readObject();
if (asn1Sequence.getValue().size() < 9) {
throw new RuntimeException("Parsed key content doesn't have the minimum required sequence size of 9");
}
BigInteger modulus = extractIntValueFrom(asn1Sequence.get(1));
BigInteger publicExponent = extractIntValueFrom(asn1Sequence.get(2));
BigInteger privateExponent = extractIntValueFrom(asn1Sequence.get(3));
BigInteger primeP = extractIntValueFrom(asn1Sequence.get(4));
BigInteger primeQ = extractIntValueFrom(asn1Sequence.get(5));
BigInteger primeExponentP = extractIntValueFrom(asn1Sequence.get(6));
BigInteger primeExponentQ = extractIntValueFrom(asn1Sequence.get(7));
BigInteger crtCoefficient = extractIntValueFrom(asn1Sequence.get(8));
return new RSAPrivateCrtKeySpec(
modulus, publicExponent, privateExponent,
primeP, primeQ, primeExponentP, primeExponentQ, crtCoefficient
);
}
}
private static BigInteger extractIntValueFrom(ASN1Object<?> asn1Object) {
if (asn1Object instanceof ASN1Integer) {
return ((ASN1Integer) asn1Object).getValue();
} else {
throw new RuntimeException(String.format(
"Unable to parse the provided value of the object type [%s]. The type should be an instance of [%s]",
asn1Object.getClass().getName(), ASN1Integer.class.getName())
);
}
}
private static Map<String, Certificate> parseCertificate(String certificateContent) throws IOException, CertificateException {
Map<String, Certificate> certificates = new HashMap<>();
Matcher certificateMatcher = CERTIFICATE_PATTERN.matcher(certificateContent);
while (certificateMatcher.find()) {
String sanitizedCertificate = certificateMatcher.group(1).replace(NEW_LINE, EMPTY).trim();
byte[] decodedCertificate = Base64.getDecoder().decode(sanitizedCertificate);
try(ByteArrayInputStream certificateAsInputStream = new ByteArrayInputStream(decodedCertificate)) {
CertificateFactory certificateFactory = CertificateFactory.getInstance(CERTIFICATE_TYPE);
Certificate certificate = certificateFactory.generateCertificate(certificateAsInputStream);
certificates.put(UUID.randomUUID().toString(), certificate);
}
}
return certificates;
}
}
这种PEM格式的更正式的参考是rfc7468第11节,它规定ASN.1内容(在de-PEM之后)是PKCS8的加密形式,定义在rfc5208第6节和附录A中,该格式略微修改为:
EncryptedPrivateKeyInfo ::= SEQUENCE {
encryptionAlgorithm AlgorithmIdentifier {{KeyEncryptionAlgorithms}},
encryptedData EncryptedData
}
EncryptedData ::= OCTET STRING
(redacted)
KeyEncryptionAlgorithms ALGORITHM-IDENTIFIER ::= {
... -- For local profiles
}
这无法解释PKCS8(PKCS12除外)常用的密钥加密算法是PKCS5 v2 rfc2898或现在的v2.1 rfc8018中定义的基于密码的加密(这里的微小差异并不重要)。特别是对于您的Q中的密钥文件,算法标识符解析(使用OpenSSL)为:
4:d=1 hl=2 l= 64 cons: SEQUENCE
6:d=2 hl=2 l= 9 prim: OBJECT :PBES2
17:d=2 hl=2 l= 51 cons: SEQUENCE
19:d=3 hl=2 l= 27 cons: SEQUENCE
21:d=4 hl=2 l= 9 prim: OBJECT :PBKDF2
32:d=4 hl=2 l= 14 cons: SEQUENCE
34:d=5 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:F7F1520689D469C6
44:d=5 hl=2 l= 2 prim: INTEGER :0800
48:d=3 hl=2 l= 20 cons: SEQUENCE
50:d=4 hl=2 l= 8 prim: OBJECT :des-ede3-cbc
60:d=4 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:9D1A44BCC1884EA7
PBES2在第6.2节中定义为包含PBKDF2密钥派生,然后是几乎所有对称的加密/解密。您应该能够看到顶级参数与附录A.4中的PBES2参数
匹配,而keyDerivationFunc部分中的参数与附录A.2中的PBKDF2参数
匹配(存在salt和迭代计数,省略了keyLength和prf),encryptionScheme部分的参数只是一个包含初始化向量(IV)的八位字节字符串,请参见附录B.2.2。
因此,要解密这个,您需要使用这些参数实现第5.2节中指定的PBKDF2密钥派生,
虽然考虑到JRE一开始通常超过100MB,而Bouncy增加了大约5MB,并且更易于使用,但我不能说我同意你的评估。
更新:我没有意识到,但是JCE的PBKDF2 SKF实际上并不做密钥推导,它只打包数据元素供合适的Cipher
算法使用——并且它没有实现我们需要的Cipher
算法。所以你需要手动编码PBKDF2,幸运的是(?)我已经用openssl和aes-cbc-256加密了Decrypt文件。把这些放在一起,下面是测试代码:
static void SO63832456ManualPKCS8EPBES2 (String[] args) throws Exception {
String file = args[0], pass = args[1];
// I use Bouncy to read because it's much easier for me,
// but you should get the same results with any ASN.1 lib
PemObject obj; try(Reader rdr = new FileReader(file);
PEMParser p = new PEMParser(rdr) ){ obj = p.readPemObject(); }
if( ! obj.getType().equals("ENCRYPTED PRIVATE KEY") ) throw new Exception ("wrong type");
org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo eki = org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo.getInstance(obj.getContent());
AlgorithmIdentifier algid = eki.getEncryptionAlgorithm();
if( ! algid.getAlgorithm().equals(PKCSObjectIdentifiers.id_PBES2) ) throw new Exception("not PBES2");
PBES2Parameters top = PBES2Parameters.getInstance(algid.getParameters());
KeyDerivationFunc kdf = top.getKeyDerivationFunc();
if( ! kdf.getAlgorithm().equals(PKCSObjectIdentifiers.id_PBKDF2) ) throw new Exception("not PBKDF2");
PBKDF2Params kdp = PBKDF2Params.getInstance(kdf.getParameters());
EncryptionScheme esa = top.getEncryptionScheme();
if( ! esa.getAlgorithm().equals(PKCSObjectIdentifiers.des_EDE3_CBC) ) throw new Exception("not DES3");
ASN1OctetString esp = ASN1OctetString.getInstance(esa.getParameters());
// these are the (only) items actually used
byte[] salt = kdp.getSalt(), iv = esp.getOctets(), edata = eki.getEncryptedData();
int iter = kdp.getIterationCount().intValueExact();
//SecretKeyFactory fact = SecretKeyFactory.getInstance("PBKDF2withHmacSHA1");
//SecretKey skey = fact.generateSecret(new PBEKeySpec(pass.toCharArray(), salt, iter, 24));
byte[] skey = PBKDF2 ("HmacSHA1", pass.getBytes("UTF-8"), salt, iter, 24);
Cipher ciph = Cipher.getInstance("DESEDE/CBC/PKCS5Padding");
ciph.init(Cipher.DECRYPT_MODE, new SecretKeySpec(skey,"DESEDE"), new IvParameterSpec(iv));
byte[] plain = ciph.doFinal(edata);
KeyFactory fac2 = KeyFactory.getInstance("RSA");
PrivateKey pkey = fac2.generatePrivate(new PKCS8EncodedKeySpec(plain));
System.out.println ( ((java.security.interfaces.RSAPrivateKey)pkey).getModulus() );
}
public static byte[] PBKDF2 (String prf, byte[] pass, byte[] salt, int iter, int len)
throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException {
byte[] result = new byte[len];
Mac mac = Mac.getInstance(prf);
mac.init (new SecretKeySpec (pass,prf));
byte[] saltcnt = Arrays.copyOf (salt, salt.length+4);
while( /*remaining*/len>0 ){
for( int o = saltcnt.length; --o>=saltcnt.length-4; ) if( ++saltcnt[o] != 0 ) break;
byte[] u = saltcnt, x = new byte[mac.getMacLength()];
for( int i = 1; i <= iter; i++ ){
u = mac.doFinal (u);
for( int o = 0; o < x.length; o++ ) x[o] ^= u[o];
}
int len2 = Math.min (len, x.length);
System.arraycopy (x,0, result,result.length-len, len2);
len -= len2;
}
return result;
}
-->
18998199686208613730227071552042680566660281525162377778227794768879762338654464000476266705180956908841709177555966832423085875590556580445428066261332223955361646420713402269532603328557674453787190302285379571808527818182992417461094551685532849252957724361115461909983939654809533488667032982867230996411860328826589806804115764031480737011900431983025490936659008858316889367140757195359233313370592669605576636360123189540118611655293825465309125578750878878980002346272931570343463549347548711194000065166346537274382220211242975801253306067862461345994215674963367480607539333468674983712264803816027753559737
布莱耶!
这会弹出以下窗口 我已经尝试过添加这里提到的权限。还用python boto3做了一些实验。然而,到目前为止没有取得任何成功。提前谢谢!
我一直在使用以下代码以编程方式合并加密的pdf文件。 最近,我在试图创建pdfReader时,在下面这行代码中发现了这个错误: com.itextpdf.text.exceptions。无效PdfException:未知加密类型R=6,位于com.itextpdf.text.pdf.PdfReader.readPdf(PdfReaders.java:738位于android.os.Handler.
问题内容: 如何从包含证书和私钥的PEM文件中以编程方式获取KeyStore?我试图在HTTPS连接中向服务器提供客户端证书。我已经确认,如果我使用openssl和keytool来获取jks文件(该文件是动态加载的),则客户端证书可以使用。我什至可以通过动态读取p12(PKCS12)文件来使其工作。 我正在考虑使用BouncyCastle的PEMReader类,但无法克服一些错误。我正在使用-Dj
是否有无论如何创建pem和pk8文件由我的应用程序本身使用java编程?
问题内容: 我需要以编程方式计算给定jar文件中已编译类,接口和枚举的数量(因此我需要三个单独的数字)。哪个API对我有帮助?(我不能使用第三方库。) 我已经尝试过非常棘手的方案,这似乎并不总是正确的。即,我将每个ZipEntry读入byte [],然后将结果提供给我的自定义类加载器,该加载器扩展了标准CalssLoader并将此byte []发送到ClassLoader.defineClass(
我有一个加密的私钥,我知道密码。 我需要用Java库解密它。 不过,我不想使用BouncyCastle,除非没有其他选择。根据之前的经验,变更太多,文档不足。 私钥的格式如下: 我相信密钥数据是Base64编码的,因为我在64个字符后看到。 我尝试了以下方法来解密密钥: 我得到这个例外 我是否将正确的参数传递给构造函数? 我怎样才能做到这一点? 我尝试了Ericsonn的建议,有一个小的变化,因为