mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 15:20:54 +03:00
Add PKCS8 parsing for encrypted PEM ASN.1 Private Keys (#713)
- Added unit tests for encrypted PKCS8 RSA Private Key
This commit is contained in:
@@ -27,7 +27,12 @@ import org.bouncycastle.openssl.PEMEncryptedKeyPair;
|
||||
import org.bouncycastle.openssl.PEMKeyPair;
|
||||
import org.bouncycastle.openssl.PEMParser;
|
||||
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
|
||||
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
|
||||
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
|
||||
import org.bouncycastle.operator.InputDecryptorProvider;
|
||||
import org.bouncycastle.operator.OperatorCreationException;
|
||||
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
|
||||
import org.bouncycastle.pkcs.PKCSException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -53,8 +58,6 @@ public class PKCS8KeyFile extends BaseFileKeyProvider {
|
||||
|
||||
protected final Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
protected char[] passphrase; // for blanking out
|
||||
|
||||
protected KeyPairConverter<PrivateKeyInfo> privateKeyInfoKeyPairConverter = new PrivateKeyInfoKeyPairConverter();
|
||||
|
||||
protected KeyPair readKeyPair()
|
||||
@@ -74,22 +77,19 @@ public class PKCS8KeyFile extends BaseFileKeyProvider {
|
||||
|
||||
if (o instanceof PEMEncryptedKeyPair) {
|
||||
final PEMEncryptedKeyPair encryptedKeyPair = (PEMEncryptedKeyPair) o;
|
||||
JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder();
|
||||
if (SecurityUtils.getSecurityProvider() != null) {
|
||||
decryptorBuilder.setProvider(SecurityUtils.getSecurityProvider());
|
||||
}
|
||||
try {
|
||||
passphrase = pwdf == null ? null : pwdf.reqPassword(resource);
|
||||
kp = pemConverter.getKeyPair(encryptedKeyPair.decryptKeyPair(decryptorBuilder.build(passphrase)));
|
||||
} finally {
|
||||
PasswordUtils.blankOut(passphrase);
|
||||
}
|
||||
final PEMKeyPair pemKeyPair = readEncryptedKeyPair(encryptedKeyPair);
|
||||
kp = pemConverter.getKeyPair(pemKeyPair);
|
||||
} else if (o instanceof PEMKeyPair) {
|
||||
kp = pemConverter.getKeyPair((PEMKeyPair) o);
|
||||
} else if (o instanceof PrivateKeyInfo) {
|
||||
final PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) o;
|
||||
final PEMKeyPair pemKeyPair = privateKeyInfoKeyPairConverter.getKeyPair(privateKeyInfo);
|
||||
kp = pemConverter.getKeyPair(pemKeyPair);
|
||||
} else if (o instanceof PKCS8EncryptedPrivateKeyInfo) {
|
||||
final PKCS8EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = (PKCS8EncryptedPrivateKeyInfo) o;
|
||||
final PrivateKeyInfo privateKeyInfo = readEncryptedPrivateKeyInfo(encryptedPrivateKeyInfo);
|
||||
final PEMKeyPair pemKeyPair = privateKeyInfoKeyPairConverter.getKeyPair(privateKeyInfo);
|
||||
kp = pemConverter.getKeyPair(pemKeyPair);
|
||||
} else {
|
||||
log.warn("Unexpected PKCS8 PEM Object [{}]", o);
|
||||
}
|
||||
@@ -114,4 +114,37 @@ public class PKCS8KeyFile extends BaseFileKeyProvider {
|
||||
public String toString() {
|
||||
return "PKCS8KeyFile{resource=" + resource + "}";
|
||||
}
|
||||
|
||||
private PEMKeyPair readEncryptedKeyPair(final PEMEncryptedKeyPair encryptedKeyPair) throws IOException {
|
||||
final JcePEMDecryptorProviderBuilder builder = new JcePEMDecryptorProviderBuilder();
|
||||
if (SecurityUtils.getSecurityProvider() != null) {
|
||||
builder.setProvider(SecurityUtils.getSecurityProvider());
|
||||
}
|
||||
char[] passphrase = null;
|
||||
try {
|
||||
passphrase = pwdf == null ? null : pwdf.reqPassword(resource);
|
||||
return encryptedKeyPair.decryptKeyPair(builder.build(passphrase));
|
||||
} finally {
|
||||
PasswordUtils.blankOut(passphrase);
|
||||
}
|
||||
}
|
||||
|
||||
private PrivateKeyInfo readEncryptedPrivateKeyInfo(final PKCS8EncryptedPrivateKeyInfo encryptedPrivateKeyInfo) throws EncryptionException {
|
||||
final JceOpenSSLPKCS8DecryptorProviderBuilder builder = new JceOpenSSLPKCS8DecryptorProviderBuilder();
|
||||
if (SecurityUtils.getSecurityProvider() != null) {
|
||||
builder.setProvider(SecurityUtils.getSecurityProvider());
|
||||
}
|
||||
char[] passphrase = null;
|
||||
try {
|
||||
passphrase = pwdf == null ? null : pwdf.reqPassword(resource);
|
||||
final InputDecryptorProvider inputDecryptorProvider = builder.build(passphrase);
|
||||
return encryptedPrivateKeyInfo.decryptPrivateKeyInfo(inputDecryptorProvider);
|
||||
} catch (final OperatorCreationException e) {
|
||||
throw new EncryptionException("Loading Password for Encrypted Private Key Failed", e);
|
||||
} catch (final PKCSException e) {
|
||||
throw new EncryptionException("Reading Encrypted Private Key Failed", e);
|
||||
} finally {
|
||||
PasswordUtils.blankOut(passphrase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,18 @@
|
||||
*/
|
||||
package net.schmizz.sshj.keyprovider;
|
||||
|
||||
import com.hierynomus.sshj.common.KeyDecryptionFailedException;
|
||||
import net.schmizz.sshj.common.KeyType;
|
||||
import net.schmizz.sshj.common.SecurityUtils;
|
||||
import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider;
|
||||
import net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile;
|
||||
import net.schmizz.sshj.userauth.password.PasswordFinder;
|
||||
import net.schmizz.sshj.userauth.password.PasswordUtils;
|
||||
import net.schmizz.sshj.util.KeyUtil;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -35,6 +40,9 @@ import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class PKCS8KeyFileTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
static final FileKeyProvider rsa = new PKCS8KeyFile();
|
||||
|
||||
static final String modulus = "a19f65e93926d9a2f5b52072db2c38c54e6cf0113d31fa92ff827b0f3bec609c45ea84264c88e64adba11ff093ed48ee0ed297757654b0884ab5a7e28b3c463bc9074b32837a2b69b61d914abf1d74ccd92b20fa44db3b31fb208c0dd44edaeb4ab097118e8ee374b6727b89ad6ce43f1b70c5a437ccebc36d2dad8ae973caad15cd89ae840fdae02cae42d241baef8fda8aa6bbaa54fd507a23338da6f06f61b34fb07d560e63fbce4a39c073e28573c2962cedb292b14b80d1b4e67b0465f2be0e38526232d0a7f88ce91a055fde082038a87ed91f3ef5ff971e30ea6cccf70d38498b186621c08f8fdceb8632992b480bf57fc218e91f2ca5936770fe9469";
|
||||
@@ -70,6 +78,25 @@ public class PKCS8KeyFileTest {
|
||||
assertEquals("RSA", provider.getPrivate().getAlgorithm());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPkcs8RsaEncrypted() throws IOException {
|
||||
final PKCS8KeyFile provider = new PKCS8KeyFile();
|
||||
final PasswordFinder passwordFinder = PasswordUtils.createOneOff("passphrase".toCharArray());
|
||||
provider.init(getReader("pkcs8-rsa-2048-encrypted"), passwordFinder);
|
||||
assertEquals("RSA", provider.getPublic().getAlgorithm());
|
||||
assertEquals("RSA", provider.getPrivate().getAlgorithm());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPkcs8RsaEncryptedIncorrectPassword() throws IOException {
|
||||
expectedException.expect(KeyDecryptionFailedException.class);
|
||||
|
||||
final PKCS8KeyFile provider = new PKCS8KeyFile();
|
||||
final PasswordFinder passwordFinder = PasswordUtils.createOneOff(String.class.getSimpleName().toCharArray());
|
||||
provider.init(getReader("pkcs8-rsa-2048-encrypted"), passwordFinder);
|
||||
provider.getPrivate();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPkcs8Ecdsa() throws IOException {
|
||||
final PKCS8KeyFile provider = new PKCS8KeyFile();
|
||||
|
||||
30
src/test/resources/keyformats/pkcs8-rsa-2048-encrypted
Normal file
30
src/test/resources/keyformats/pkcs8-rsa-2048-encrypted
Normal file
@@ -0,0 +1,30 @@
|
||||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIizi0oXD8HM0CAggA
|
||||
MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBAvGR0fYj/JgxVzxslaeVRtBIIE
|
||||
0IiXAnJ0YtAHia4QOziIghhJ+qdJXLvAssQ5gK6wPqMUWV8Zut+/7mb0mObI5qyb
|
||||
afv4GmS5TyI5c+gYHzXGt++Lqp9JSdisdLEPsCFHYtRd6ujC1D2EXaFOpTRBFFuw
|
||||
QCQa+qQFqEcDu31IFo7Obj86V2NIE8O8zWJvtTih5EloEaJmZK+lVjsXWRWRRFqh
|
||||
ZNU7PanlADb6N+xXyn2VnQbiFouOCTvmCN8bzvpCg8wJMyfMJU4gJbJE2cVD0nYI
|
||||
sz63ZO1I1ljhx7FKsTlnza6PsGbL3StN8eTlNUEL6SGiMllPXdKXXte5VqIJzoJ5
|
||||
a8OAC9Gmou6YRSttCGSaUCvKGCl0iEAe86vv1PiM873DNuer/IgUGXGBSk8uG5qm
|
||||
7PTYs8kzvovcuMHUg1O2t5aKCCVah3o2jfY+koeQtBq01kP5jQ0VB8w0trKsT614
|
||||
BJhN7Es9AkNya/qvDoysmebc0NJQt8rDLkvumn2jlWCDVM56Xl4P/qknllGilSyf
|
||||
wWClZzsd5Q8MflbjqCwiYp19ZK4IHW7Y6hxdBZWWFd5oWw47GwWPTLdgnsGtp0dW
|
||||
TK3IamTLO5T2BK2NdctXZ9CCn8ReuRVBA2jTNp6PWrcVxhHD4uUr1FE++e2Q+cQM
|
||||
iljqis/md4lmpb74lhCNbhxqiFoVSe3XzvVtNxVXNYqxb9PqRKxWXWtyYk7/jo2u
|
||||
Q284skAMcPvuGkl9Ba5RTup7t4V/eQfPTHeY/rnKyTd+hlUb+Tc26EOIKwSCxSZy
|
||||
q36h4JHVjM3BRQzNMpH/GyuW1+qUw9SFOcuqtTwOxDe5rUis0sFXQyyXc/3IZcYZ
|
||||
HSQzdmzFXNXysVRox5+0AfuEAOj8bc4bn0Sr7UQrYKCtJHOnl01mT51pdRCZJV/j
|
||||
fRYIbxJG0yqCxPoEh0sh3fwuiNkkHHTsaDZ8aXyXL1K0C+OzXlYkbc8i8iaW7UOc
|
||||
ymPD7BdmLLSADH2nb3M+QORF5IJtQ+8j11B+App2V7ao35azGScCda9rDMZCPl/6
|
||||
4OwPtRWEOlUzZVq3Uid61w3Expc4zaZ4gyXG9q/UU4/TabPt4fVv6dMTb5tpOinG
|
||||
CgBqBK5ihDOFUJxrFZ5OMy9SjJvecDslRrSxykPCHMx93iGfZlPmYfLfMa/AW3nr
|
||||
uXVTtd6Pk4P7z4/LdsJqqlHVKqkaZI5nrCrUMQwSjhAOBhEkipyQWj+3OYgfheIi
|
||||
FbNP0hdSvez5JCWin/aoc3Xe8oKM0i2liM7VcNKbKwPNULTmj7g08LUxjwT8pzPj
|
||||
z1kjlKEJgEF/d+OQ6nhU2moKvIPyxzi/+FBfVG2H8Tm+57RlwRSsv2pe7XAn+0jW
|
||||
xLSQFHgvZj3kcN9kT4o8A812kWgn3HV3ve2s/1sVKPvLrU8AjAHWW0pJ8fCGqAF+
|
||||
Q9cWPwtd2D3KJemdUUXPe2Vd/WbtfRmWzPtsVFdA4BRODgOIQNGNnjv6mkezpHrM
|
||||
ixXKvSqDf1GuYNzLTNo91EmaAGgSA0T1ZMEesPXLhxxBCdw8Dyq90Uzp3Vzbiia6
|
||||
Sy5UKaoU73xI3HfPo5d77n5vdy9UoJ5Oje6oq4DXB3WstDK/WBeaju5eCezVBhSA
|
||||
ksBiPIHfhkDdnTOzrCATAQBRO92VQV4b3yEXic3Q6FIH
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
||||
Reference in New Issue
Block a user