/* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.asn;
import java.math.BigInteger;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.cryptacular.EncodingException;
import org.cryptacular.pbe.OpenSSLAlgorithm;
import org.cryptacular.pbe.OpenSSLEncryptionScheme;
import org.cryptacular.util.ByteUtil;
import org.cryptacular.util.CodecUtil;
import org.cryptacular.util.PemUtil;
/**
* Decrypts PEM-encoded OpenSSL "traditional" format private keys.
*
* @author Middleware Services
*/
public class OpenSSLPrivateKeyDecoder extends AbstractPrivateKeyDecoder<AsymmetricKeyParameter>
{
@Override
protected byte[] decryptKey(final byte[] encrypted, final char[] password)
{
final String pem = new String(encrypted, ByteUtil.ASCII_CHARSET);
final int start = pem.indexOf(PemUtil.DEK_INFO);
final int eol = pem.indexOf('\n', start);
final String[] dekInfo = pem.substring(start + 10, eol).split(",");
final String alg = dekInfo[0];
final byte[] iv = CodecUtil.hex(dekInfo[1]);
final byte[] bytes = PemUtil.decode(encrypted);
return new OpenSSLEncryptionScheme(OpenSSLAlgorithm.fromAlgorithmId(alg), iv, password).decrypt(bytes);
}
@Override
protected AsymmetricKeyParameter decodeASN1(final byte[] encoded)
{
final ASN1Object o;
try {
o = new ASN1InputStream(encoded).readObject();
} catch (Exception e) {
throw new EncodingException("Invalid encoded key", e);
}
final AsymmetricKeyParameter key;
if (o instanceof ASN1ObjectIdentifier) {
// EC private key with named curve in the default OpenSSL format emitted
// by
//
// openssl ecparam -name xxxx -genkey
//
// which is the concatenation of the named curve OID and a sequence of 1
// containing the private point
final ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(o);
final int len = encoded[1];
final byte[] privatePart = new byte[encoded.length - len - 2];
System.arraycopy(encoded, len + 2, privatePart, 0, privatePart.length);
final ASN1Sequence seq = ASN1Sequence.getInstance(privatePart);
final X9ECParameters params = ECUtil.getNamedCurveByOid(oid);
key = new ECPrivateKeyParameters(
ASN1Integer.getInstance(seq.getObjectAt(0)).getValue(),
new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH(), params.getSeed()));
} else {
// OpenSSL "traditional" format is an ASN.1 sequence of key parameters
// Detect key type based on number and types of parameters:
// RSA -> {version, mod, pubExp, privExp, prime1, prime2, exp1, exp2, c}
// DSA -> {version, p, q, g, pubExp, privExp}
// EC -> {version, privateKey, parameters, publicKey}
final ASN1Sequence sequence = ASN1Sequence.getInstance(o);
if (sequence.size() == 9) {
// RSA private certificate key
key = new RSAPrivateCrtKeyParameters(
ASN1Integer.getInstance(sequence.getObjectAt(1)).getValue(),
ASN1Integer.getInstance(sequence.getObjectAt(2)).getValue(),
ASN1Integer.getInstance(sequence.getObjectAt(3)).getValue(),
ASN1Integer.getInstance(sequence.getObjectAt(4)).getValue(),
ASN1Integer.getInstance(sequence.getObjectAt(5)).getValue(),
ASN1Integer.getInstance(sequence.getObjectAt(6)).getValue(),
ASN1Integer.getInstance(sequence.getObjectAt(7)).getValue(),
ASN1Integer.getInstance(sequence.getObjectAt(8)).getValue());
} else if (sequence.size() == 6) {
// DSA private key
key = new DSAPrivateKeyParameters(
ASN1Integer.getInstance(sequence.getObjectAt(5)).getValue(),
new DSAParameters(
ASN1Integer.getInstance(sequence.getObjectAt(1)).getValue(),
ASN1Integer.getInstance(sequence.getObjectAt(2)).getValue(),
ASN1Integer.getInstance(sequence.getObjectAt(3)).getValue()));
} else if (sequence.size() == 4) {
// EC private key with explicit curve
final X9ECParameters params = X9ECParameters.getInstance(
ASN1TaggedObject.getInstance(sequence.getObjectAt(2)).getObject());
key = new ECPrivateKeyParameters(
new BigInteger(ASN1OctetString.getInstance(sequence.getObjectAt(1)).getOctets()),
new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH(), params.getSeed()));
} else {
throw new EncodingException("Invalid OpenSSL traditional private key format.");
}
}
return key;
}
}