/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.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.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2010 Hiroshi Nakamura <nahi@ruby-lang.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.ext.openssl.impl;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.spec.DHParameterSpec;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.sec.ECPrivateKeyStructure;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.jruby.ext.openssl.SecurityHelper;
/**
*
* Handles PKey related ASN.1 handling.
*
* @author <a href="mailto:nahi@ruby-lang.org">Hiroshi Nakamura</a>
*/
public class PKey {
public static KeyPair readPrivateKey(final byte[] input, final String type)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
KeySpec pubSpec; KeySpec privSpec;
ASN1Sequence seq = (ASN1Sequence) new ASN1InputStream(input).readObject();
if ( type.equals("RSA") ) {
ASN1Integer mod = (ASN1Integer) seq.getObjectAt(1);
ASN1Integer pubExp = (ASN1Integer) seq.getObjectAt(2);
ASN1Integer privExp = (ASN1Integer) seq.getObjectAt(3);
ASN1Integer p1 = (ASN1Integer) seq.getObjectAt(4);
ASN1Integer p2 = (ASN1Integer) seq.getObjectAt(5);
ASN1Integer exp1 = (ASN1Integer) seq.getObjectAt(6);
ASN1Integer exp2 = (ASN1Integer) seq.getObjectAt(7);
ASN1Integer crtCoef = (ASN1Integer) seq.getObjectAt(8);
pubSpec = new RSAPublicKeySpec(mod.getValue(), pubExp.getValue());
privSpec = new RSAPrivateCrtKeySpec(mod.getValue(), pubExp.getValue(), privExp.getValue(), p1.getValue(), p2.getValue(), exp1.getValue(),
exp2.getValue(), crtCoef.getValue());
}
else if ( type.equals("DSA") ) {
ASN1Integer p = (ASN1Integer) seq.getObjectAt(1);
ASN1Integer q = (ASN1Integer) seq.getObjectAt(2);
ASN1Integer g = (ASN1Integer) seq.getObjectAt(3);
ASN1Integer y = (ASN1Integer) seq.getObjectAt(4);
ASN1Integer x = (ASN1Integer) seq.getObjectAt(5);
privSpec = new DSAPrivateKeySpec(x.getValue(), p.getValue(), q.getValue(), g.getValue());
pubSpec = new DSAPublicKeySpec(y.getValue(), p.getValue(), q.getValue(), g.getValue());
}
else if ( type.equals("ECDSA") ) {
return readECPrivateKey(input);
}
else {
throw new IllegalStateException("unsupported type: " + type);
}
KeyFactory fact = SecurityHelper.getKeyFactory(type);
return new KeyPair(fact.generatePublic(pubSpec), fact.generatePrivate(privSpec));
}
// d2i_PrivateKey_bio
public static KeyPair readPrivateKey(byte[] input) throws IOException,
NoSuchAlgorithmException, InvalidKeySpecException {
KeyPair key = null;
try {
key = readRSAPrivateKey(input);
}
catch (NoSuchAlgorithmException e) { throw e; /* should not happen */ }
catch (InvalidKeySpecException e) {
// ignore
}
if (key == null) {
try {
key = readDSAPrivateKey(input);
}
catch (NoSuchAlgorithmException e) { throw e; /* should not happen */ }
catch (InvalidKeySpecException e) {
// ignore
}
}
return key;
}
// d2i_PUBKEY_bio
public static PublicKey readPublicKey(byte[] input) throws IOException,
NoSuchAlgorithmException, InvalidKeySpecException {
PublicKey key = null;
try {
key = readRSAPublicKey(input);
}
catch (NoSuchAlgorithmException e) { throw e; /* should not happen */ }
catch (InvalidKeySpecException e) {
// ignore
}
if (key == null) {
try {
key = readDSAPublicKey(input);
}
catch (NoSuchAlgorithmException e) { throw e; /* should not happen */ }
catch (InvalidKeySpecException e) {
// ignore
}
}
return key;
}
// d2i_RSAPrivateKey_bio
public static KeyPair readRSAPrivateKey(final byte[] input)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
return readRSAPrivateKey(SecurityHelper.getKeyFactory("RSA"), input);
}
public static KeyPair readRSAPrivateKey(final KeyFactory rsaFactory, final byte[] input)
throws IOException, InvalidKeySpecException {
ASN1Sequence seq = (ASN1Sequence) new ASN1InputStream(input).readObject();
if ( seq.size() == 9 ) {
BigInteger mod = ((ASN1Integer) seq.getObjectAt(1)).getValue();
BigInteger pubexp = ((ASN1Integer) seq.getObjectAt(2)).getValue();
BigInteger privexp = ((ASN1Integer) seq.getObjectAt(3)).getValue();
BigInteger primep = ((ASN1Integer) seq.getObjectAt(4)).getValue();
BigInteger primeq = ((ASN1Integer) seq.getObjectAt(5)).getValue();
BigInteger primeep = ((ASN1Integer) seq.getObjectAt(6)).getValue();
BigInteger primeeq = ((ASN1Integer) seq.getObjectAt(7)).getValue();
BigInteger crtcoeff = ((ASN1Integer) seq.getObjectAt(8)).getValue();
PrivateKey priv = rsaFactory.generatePrivate(new RSAPrivateCrtKeySpec(mod, pubexp, privexp, primep, primeq, primeep, primeeq, crtcoeff));
PublicKey pub = rsaFactory.generatePublic(new RSAPublicKeySpec(mod, pubexp));
return new KeyPair(pub, priv);
}
return null;
}
// d2i_RSAPublicKey_bio
public static PublicKey readRSAPublicKey(final byte[] input)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
return readRSAPublicKey(SecurityHelper.getKeyFactory("RSA"), input);
}
public static PublicKey readRSAPublicKey(final KeyFactory rsaFactory, final byte[] input)
throws IOException, InvalidKeySpecException {
ASN1Sequence seq = (ASN1Sequence) new ASN1InputStream(input).readObject();
if ( seq.size() == 2 ) {
BigInteger mod = ((ASN1Integer) seq.getObjectAt(0)).getValue();
BigInteger pubexp = ((ASN1Integer) seq.getObjectAt(1)).getValue();
return rsaFactory.generatePublic(new RSAPublicKeySpec(mod, pubexp));
}
return null;
}
// d2i_DSAPrivateKey_bio
public static KeyPair readDSAPrivateKey(final byte[] input)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
return readDSAPrivateKey(SecurityHelper.getKeyFactory("DSA"), input);
}
public static KeyPair readDSAPrivateKey(final KeyFactory dsaFactory, final byte[] input)
throws IOException, InvalidKeySpecException {
ASN1Sequence seq = (ASN1Sequence) new ASN1InputStream(input).readObject();
if ( seq.size() == 6 ) {
BigInteger p = ((ASN1Integer) seq.getObjectAt(1)).getValue();
BigInteger q = ((ASN1Integer) seq.getObjectAt(2)).getValue();
BigInteger g = ((ASN1Integer) seq.getObjectAt(3)).getValue();
BigInteger y = ((ASN1Integer) seq.getObjectAt(4)).getValue();
BigInteger x = ((ASN1Integer) seq.getObjectAt(5)).getValue();
PrivateKey priv = dsaFactory.generatePrivate(new DSAPrivateKeySpec(x, p, q, g));
PublicKey pub = dsaFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
return new KeyPair(pub, priv);
}
return null;
}
// d2i_DSA_PUBKEY_bio
public static PublicKey readDSAPublicKey(final byte[] input)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
return readDSAPublicKey(SecurityHelper.getKeyFactory("DSA"), input);
}
public static PublicKey readDSAPublicKey(final KeyFactory dsaFactory, final byte[] input)
throws IOException, InvalidKeySpecException {
ASN1Sequence seq = (ASN1Sequence) new ASN1InputStream(input).readObject();
if ( seq.size() == 4 ) {
BigInteger y = ((ASN1Integer) seq.getObjectAt(0)).getValue();
BigInteger p = ((ASN1Integer) seq.getObjectAt(1)).getValue();
BigInteger q = ((ASN1Integer) seq.getObjectAt(2)).getValue();
BigInteger g = ((ASN1Integer) seq.getObjectAt(3)).getValue();
return dsaFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
}
return null;
}
// d2i_DHparams_bio
public static DHParameterSpec readDHParameter(final byte[] input) throws IOException {
ASN1InputStream aIn = new ASN1InputStream(input);
ASN1Sequence seq = (ASN1Sequence) aIn.readObject();
BigInteger p = ((ASN1Integer) seq.getObjectAt(0)).getValue();
BigInteger g = ((ASN1Integer) seq.getObjectAt(1)).getValue();
return new DHParameterSpec(p, g);
}
public static KeyPair readECPrivateKey(final byte[] input)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
return readECPrivateKey(SecurityHelper.getKeyFactory("ECDSA"), input);
}
public static KeyPair readECPrivateKey(final KeyFactory ecFactory, final byte[] input)
throws IOException, InvalidKeySpecException {
try {
ECPrivateKeyStructure pKey = new ECPrivateKeyStructure((ASN1Sequence) ASN1Primitive.fromByteArray(input));
AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, pKey.getParameters());
PrivateKeyInfo privInfo = new PrivateKeyInfo(algId, pKey.toASN1Primitive());
SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(algId, pKey.getPublicKey().getBytes());
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(privInfo.getEncoded());
X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubInfo.getEncoded());
//KeyFactory fact = KeyFactory.getInstance("ECDSA", provider);
ECPrivateKey privateKey = (ECPrivateKey) ecFactory.generatePrivate(privSpec);
if ( algId.getParameters() instanceof ASN1ObjectIdentifier ) {
privateKey = ECPrivateKeyWithName.wrap(privateKey, (ASN1ObjectIdentifier) algId.getParameters());
}
return new KeyPair(ecFactory.generatePublic(pubSpec), privateKey);
}
catch (ClassCastException ex) {
throw new IOException("wrong ASN.1 object found in stream", ex);
}
//catch (Exception ex) {
// throw new IOException("problem parsing EC private key: " + ex);
//}
}
public static byte[] toDerRSAKey(RSAPublicKey pubKey, RSAPrivateCrtKey privKey) throws IOException {
ASN1EncodableVector vec = new ASN1EncodableVector();
if ( pubKey != null && privKey == null ) {
vec.add(new ASN1Integer(pubKey.getModulus()));
vec.add(new ASN1Integer(pubKey.getPublicExponent()));
}
else {
vec.add(new ASN1Integer(BigInteger.ZERO));
vec.add(new ASN1Integer(privKey.getModulus()));
vec.add(new ASN1Integer(privKey.getPublicExponent()));
vec.add(new ASN1Integer(privKey.getPrivateExponent()));
vec.add(new ASN1Integer(privKey.getPrimeP()));
vec.add(new ASN1Integer(privKey.getPrimeQ()));
vec.add(new ASN1Integer(privKey.getPrimeExponentP()));
vec.add(new ASN1Integer(privKey.getPrimeExponentQ()));
vec.add(new ASN1Integer(privKey.getCrtCoefficient()));
}
return new DLSequence(vec).getEncoded();
}
public static byte[] toDerDSAKey(DSAPublicKey pubKey, DSAPrivateKey privKey) throws IOException {
if ( pubKey != null && privKey == null ) {
return pubKey.getEncoded();
}
if ( privKey != null && pubKey != null ) {
ASN1EncodableVector vec = new ASN1EncodableVector();
final DSAParams params = privKey.getParams();
vec.add(new ASN1Integer(BigInteger.ZERO));
vec.add(new ASN1Integer(params.getP()));
vec.add(new ASN1Integer(params.getQ()));
vec.add(new ASN1Integer(params.getG()));
vec.add(new ASN1Integer(pubKey.getY()));
vec.add(new ASN1Integer(privKey.getX()));
return new DLSequence(vec).getEncoded();
}
if ( privKey == null ) {
throw new IllegalArgumentException("private key as well as public key are null");
}
return privKey.getEncoded();
}
public static byte[] toDerDHKey(BigInteger p, BigInteger g) throws IOException {
ASN1EncodableVector vec = new ASN1EncodableVector();
if ( p != null ) vec.add( new ASN1Integer(p) );
if ( g != null ) vec.add( new ASN1Integer(g) );
return new DLSequence(vec).getEncoded();
}
}