-
Notifications
You must be signed in to change notification settings - Fork 601
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enhanced PKCS8 parsing to support PEM ASN.1 Private Keys
- Loading branch information
1 parent
4d9665b
commit a64561c
Showing
10 changed files
with
427 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
...in/java/net/schmizz/sshj/userauth/keyprovider/pkcs/DSAPrivateKeyInfoKeyPairConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* | ||
* Copyright (C)2021 - SSHJ Contributors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package net.schmizz.sshj.userauth.keyprovider.pkcs; | ||
|
||
import org.bouncycastle.asn1.ASN1Integer; | ||
import org.bouncycastle.asn1.ASN1ObjectIdentifier; | ||
import org.bouncycastle.asn1.ASN1Sequence; | ||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; | ||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier; | ||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; | ||
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; | ||
import org.bouncycastle.crypto.params.DSAParameters; | ||
import org.bouncycastle.openssl.PEMKeyPair; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.io.IOException; | ||
import java.math.BigInteger; | ||
import java.util.Objects; | ||
|
||
/** | ||
* Key Pair Converter from DSA Private Key Information to PEM Key Pair | ||
*/ | ||
class DSAPrivateKeyInfoKeyPairConverter implements KeyPairConverter<PrivateKeyInfo> { | ||
private static final Logger logger = LoggerFactory.getLogger(DSAPrivateKeyInfoKeyPairConverter.class); | ||
|
||
private static final int P_INDEX = 0; | ||
|
||
private static final int Q_INDEX = 1; | ||
|
||
private static final int G_INDEX = 2; | ||
|
||
/** | ||
* Get PEM Key Pair calculating DSA Public Key from DSA Private Key Information | ||
* | ||
* @param privateKeyInfo DSA Private Key Information | ||
* @return PEM Key Pair | ||
* @throws IOException Thrown on Public Key parsing failures | ||
*/ | ||
@Override | ||
public PEMKeyPair getKeyPair(final PrivateKeyInfo privateKeyInfo) throws IOException { | ||
Objects.requireNonNull(privateKeyInfo, "Private Key Info required"); | ||
final AlgorithmIdentifier algorithmIdentifier = privateKeyInfo.getPrivateKeyAlgorithm(); | ||
final ASN1ObjectIdentifier algorithm = algorithmIdentifier.getAlgorithm(); | ||
if (X9ObjectIdentifiers.id_dsa.equals(algorithm)) { | ||
logger.debug("DSA Algorithm Found [{}]", algorithm); | ||
} else { | ||
throw new IllegalArgumentException(String.format("DSA Algorithm OID required [%s]", algorithm)); | ||
} | ||
final ASN1Integer encodedPublicKey = getEncodedPublicKey(privateKeyInfo); | ||
final SubjectPublicKeyInfo subjectPublicKeyInfo = new SubjectPublicKeyInfo(algorithmIdentifier, encodedPublicKey); | ||
return new PEMKeyPair(subjectPublicKeyInfo, privateKeyInfo); | ||
} | ||
|
||
/** | ||
* Get ASN.1 Encoded Public Key calculated according to RFC 6979 Section 2.2 | ||
* | ||
* @param privateKeyInfo DSA Private Key Information | ||
* @return ASN.1 Encoded DSA Public Key | ||
* @throws IOException Thrown on failures parsing private key | ||
*/ | ||
private ASN1Integer getEncodedPublicKey(final PrivateKeyInfo privateKeyInfo) throws IOException { | ||
final ASN1Integer privateKey = ASN1Integer.getInstance(privateKeyInfo.parsePrivateKey()); | ||
final AlgorithmIdentifier algorithmIdentifier = privateKeyInfo.getPrivateKeyAlgorithm(); | ||
final DSAParameters dsaParameters = getDsaParameters(algorithmIdentifier); | ||
final BigInteger publicKey = dsaParameters.getG().modPow(privateKey.getValue(), dsaParameters.getP()); | ||
return new ASN1Integer(publicKey); | ||
} | ||
|
||
private DSAParameters getDsaParameters(final AlgorithmIdentifier algorithmIdentifier) { | ||
final ASN1Sequence sequence = ASN1Sequence.getInstance(algorithmIdentifier.getParameters()); | ||
final ASN1Integer p = ASN1Integer.getInstance(sequence.getObjectAt(P_INDEX)); | ||
final ASN1Integer q = ASN1Integer.getInstance(sequence.getObjectAt(Q_INDEX)); | ||
final ASN1Integer g = ASN1Integer.getInstance(sequence.getObjectAt(G_INDEX)); | ||
return new DSAParameters(p.getValue(), q.getValue(), g.getValue()); | ||
} | ||
} |
90 changes: 90 additions & 0 deletions
90
.../java/net/schmizz/sshj/userauth/keyprovider/pkcs/ECDSAPrivateKeyInfoKeyPairConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* | ||
* Copyright (C)2021 - SSHJ Contributors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package net.schmizz.sshj.userauth.keyprovider.pkcs; | ||
|
||
import org.bouncycastle.asn1.ASN1ObjectIdentifier; | ||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; | ||
import org.bouncycastle.asn1.sec.ECPrivateKey; | ||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier; | ||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; | ||
import org.bouncycastle.asn1.x9.X9ECParameters; | ||
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; | ||
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; | ||
import org.bouncycastle.math.ec.ECMultiplier; | ||
import org.bouncycastle.math.ec.ECPoint; | ||
import org.bouncycastle.math.ec.FixedPointCombMultiplier; | ||
import org.bouncycastle.openssl.PEMKeyPair; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.io.IOException; | ||
import java.math.BigInteger; | ||
import java.util.Objects; | ||
|
||
/** | ||
* Key Pair Converter from ECDSA Private Key Information to PEM Key Pair | ||
*/ | ||
class ECDSAPrivateKeyInfoKeyPairConverter implements KeyPairConverter<PrivateKeyInfo> { | ||
private static final Logger logger = LoggerFactory.getLogger(ECDSAPrivateKeyInfoKeyPairConverter.class); | ||
|
||
private static final boolean POINT_COMPRESSED = false; | ||
|
||
/** | ||
* Get PEM Key Pair calculating ECDSA Public Key from ECDSA Private Key Information | ||
* | ||
* @param privateKeyInfo ECDSA Private Key Information | ||
* @return PEM Key Pair | ||
* @throws IOException Thrown on Public Key parsing failures | ||
*/ | ||
@Override | ||
public PEMKeyPair getKeyPair(final PrivateKeyInfo privateKeyInfo) throws IOException { | ||
Objects.requireNonNull(privateKeyInfo, "Private Key Info required"); | ||
final AlgorithmIdentifier algorithmIdentifier = privateKeyInfo.getPrivateKeyAlgorithm(); | ||
final ASN1ObjectIdentifier algorithm = algorithmIdentifier.getAlgorithm(); | ||
if (X9ObjectIdentifiers.id_ecPublicKey.equals(algorithm)) { | ||
logger.debug("ECDSA Algorithm Found [{}]", algorithm); | ||
} else { | ||
throw new IllegalArgumentException(String.format("ECDSA Algorithm OID required [%s]", algorithm)); | ||
} | ||
final byte[] encodedPublicKey = getEncodedPublicKey(privateKeyInfo); | ||
final SubjectPublicKeyInfo subjectPublicKeyInfo = new SubjectPublicKeyInfo(algorithmIdentifier, encodedPublicKey); | ||
return new PEMKeyPair(subjectPublicKeyInfo, privateKeyInfo); | ||
} | ||
|
||
/** | ||
* Get Encoded Elliptic Curve Public Key calculated according to RFC 6979 Section 2.2 | ||
* | ||
* @param privateKeyInfo ECDSA Private Key Information | ||
* @return Encoded Elliptic Curve Public Key | ||
* @throws IOException Thrown on failures parsing private key | ||
*/ | ||
private byte[] getEncodedPublicKey(final PrivateKeyInfo privateKeyInfo) throws IOException { | ||
final X9ECParameters parameters = getParameters(privateKeyInfo.getPrivateKeyAlgorithm()); | ||
final ECPrivateKey ecPrivateKey = ECPrivateKey.getInstance(privateKeyInfo.parsePrivateKey()); | ||
final ECPoint publicKey = getPublicKey(parameters, ecPrivateKey.getKey()); | ||
return publicKey.getEncoded(POINT_COMPRESSED); | ||
} | ||
|
||
private X9ECParameters getParameters(final AlgorithmIdentifier algorithmIdentifier) { | ||
final ASN1ObjectIdentifier encodedParameters = ASN1ObjectIdentifier.getInstance(algorithmIdentifier.getParameters()); | ||
return ECUtil.getNamedCurveByOid(encodedParameters); | ||
} | ||
|
||
private ECPoint getPublicKey(final X9ECParameters parameters, final BigInteger privateKey) { | ||
final ECMultiplier multiplier = new FixedPointCombMultiplier(); | ||
return multiplier.multiply(parameters.getG(), privateKey); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
src/main/java/net/schmizz/sshj/userauth/keyprovider/pkcs/KeyPairConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* Copyright (C)2021 - SSHJ Contributors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package net.schmizz.sshj.userauth.keyprovider.pkcs; | ||
|
||
import org.bouncycastle.openssl.PEMKeyPair; | ||
|
||
import java.io.IOException; | ||
|
||
/** | ||
* Converter from typed object to PEM Key Pair | ||
* @param <T> Object Type | ||
*/ | ||
public interface KeyPairConverter<T> { | ||
/** | ||
* Get PEM Key Pair from typed object | ||
* | ||
* @param object Typed Object | ||
* @return PEM Key Pair | ||
* @throws IOException Thrown on conversion failures | ||
*/ | ||
PEMKeyPair getKeyPair(T object) throws IOException; | ||
} |
61 changes: 61 additions & 0 deletions
61
src/main/java/net/schmizz/sshj/userauth/keyprovider/pkcs/PrivateKeyInfoKeyPairConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* Copyright (C)2021 - SSHJ Contributors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package net.schmizz.sshj.userauth.keyprovider.pkcs; | ||
|
||
import org.bouncycastle.asn1.ASN1ObjectIdentifier; | ||
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; | ||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; | ||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier; | ||
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; | ||
import org.bouncycastle.openssl.PEMKeyPair; | ||
|
||
import java.io.IOException; | ||
import java.util.Objects; | ||
|
||
/** | ||
* Key Pair Converter for Private Key Information using known Algorithm Object Identifiers | ||
*/ | ||
public class PrivateKeyInfoKeyPairConverter implements KeyPairConverter<PrivateKeyInfo> { | ||
private DSAPrivateKeyInfoKeyPairConverter dsaPrivateKeyInfoKeyPairConverter = new DSAPrivateKeyInfoKeyPairConverter(); | ||
|
||
private ECDSAPrivateKeyInfoKeyPairConverter ecdsaPrivateKeyInfoKeyPairConverter = new ECDSAPrivateKeyInfoKeyPairConverter(); | ||
|
||
private RSAPrivateKeyInfoKeyPairConverter rsaPrivateKeyInfoKeyPairConverter = new RSAPrivateKeyInfoKeyPairConverter(); | ||
|
||
/** | ||
* Get PEM Key Pair delegating to configured converters based on Algorithm Object Identifier | ||
* | ||
* @param privateKeyInfo Private Key Information | ||
* @return PEM Key Pair | ||
* @throws IOException Thrown on conversion failures | ||
*/ | ||
@Override | ||
public PEMKeyPair getKeyPair(final PrivateKeyInfo privateKeyInfo) throws IOException { | ||
Objects.requireNonNull(privateKeyInfo, "Private Key Info required"); | ||
final AlgorithmIdentifier algorithmIdentifier = privateKeyInfo.getPrivateKeyAlgorithm(); | ||
final ASN1ObjectIdentifier algorithm = algorithmIdentifier.getAlgorithm(); | ||
|
||
if (PKCSObjectIdentifiers.rsaEncryption.equals(algorithm)) { | ||
return rsaPrivateKeyInfoKeyPairConverter.getKeyPair(privateKeyInfo); | ||
} else if (X9ObjectIdentifiers.id_ecPublicKey.equals(algorithm)) { | ||
return ecdsaPrivateKeyInfoKeyPairConverter.getKeyPair(privateKeyInfo); | ||
} else if (X9ObjectIdentifiers.id_dsa.equals(algorithm)) { | ||
return dsaPrivateKeyInfoKeyPairConverter.getKeyPair(privateKeyInfo); | ||
} else { | ||
throw new IllegalArgumentException(String.format("Unsupported Algorithm [%s]", algorithm)); | ||
} | ||
} | ||
} |
65 changes: 65 additions & 0 deletions
65
...in/java/net/schmizz/sshj/userauth/keyprovider/pkcs/RSAPrivateKeyInfoKeyPairConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* | ||
* Copyright (C)2021 - SSHJ Contributors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package net.schmizz.sshj.userauth.keyprovider.pkcs; | ||
|
||
import org.bouncycastle.asn1.ASN1ObjectIdentifier; | ||
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; | ||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; | ||
import org.bouncycastle.asn1.pkcs.RSAPrivateKey; | ||
import org.bouncycastle.asn1.pkcs.RSAPublicKey; | ||
import org.bouncycastle.asn1.x509.AlgorithmIdentifier; | ||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; | ||
import org.bouncycastle.openssl.PEMKeyPair; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.io.IOException; | ||
import java.util.Objects; | ||
|
||
/** | ||
* Key Pair Converter from RSA Private Key Information to PEM Key Pair | ||
*/ | ||
class RSAPrivateKeyInfoKeyPairConverter implements KeyPairConverter<PrivateKeyInfo> { | ||
private static final Logger logger = LoggerFactory.getLogger(RSAPrivateKeyInfoKeyPairConverter.class); | ||
|
||
/** | ||
* Get PEM Key Pair parsing RSA Public Key attributes from RSA Private Key Information | ||
* | ||
* @param privateKeyInfo RSA Private Key Information | ||
* @return PEM Key Pair | ||
* @throws IOException Thrown on Public Key parsing failures | ||
*/ | ||
@Override | ||
public PEMKeyPair getKeyPair(final PrivateKeyInfo privateKeyInfo) throws IOException { | ||
Objects.requireNonNull(privateKeyInfo, "Private Key Info required"); | ||
final AlgorithmIdentifier algorithmIdentifier = privateKeyInfo.getPrivateKeyAlgorithm(); | ||
final ASN1ObjectIdentifier algorithm = algorithmIdentifier.getAlgorithm(); | ||
if (PKCSObjectIdentifiers.rsaEncryption.equals(algorithm)) { | ||
logger.debug("RSA Algorithm Found [{}]", algorithm); | ||
} else { | ||
throw new IllegalArgumentException(String.format("RSA Algorithm OID required [%s]", algorithm)); | ||
} | ||
|
||
final RSAPublicKey rsaPublicKey = getRsaPublicKey(privateKeyInfo); | ||
final SubjectPublicKeyInfo subjectPublicKeyInfo = new SubjectPublicKeyInfo(algorithmIdentifier, rsaPublicKey); | ||
return new PEMKeyPair(subjectPublicKeyInfo, privateKeyInfo); | ||
} | ||
|
||
private RSAPublicKey getRsaPublicKey(final PrivateKeyInfo privateKeyInfo) throws IOException { | ||
final RSAPrivateKey rsaPrivateKey = RSAPrivateKey.getInstance(privateKeyInfo.parsePrivateKey()); | ||
return new RSAPublicKey(rsaPrivateKey.getModulus(), rsaPrivateKey.getPublicExponent()); | ||
} | ||
} |
Oops, something went wrong.