org.xipki.security.KeyUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.xipki.security.KeyUtil.java

Source

/*
 *
 * This file is part of the XiPKI project.
 * Copyright (c) 2014 - 2015 Lijun Liao
 * Author: Lijun Liao
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation with the addition of the
 * following permission added to Section 15 as permitted in Section 7(a):
 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
 * THE AUTHOR LIJUN LIAO. LIJUN LIAO DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
 * OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License.
 *
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the XiPKI software without
 * disclosing the source code of your own applications.
 *
 * For more information, please contact Lijun Liao at this
 * address: lijun.liao@gmail.com
 */

package org.xipki.security;

import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
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.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.DSAParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAKeyGenParameterSpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.generators.DSAParametersGenerator;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DSAParameterGenerationParameters;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.dsa.DSAUtil;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcContentVerifierProviderBuilder;
import org.bouncycastle.operator.bc.BcDSAContentVerifierProviderBuilder;
import org.xipki.security.bcext.BcRSAContentVerifierProviderBuilder;
import org.xipki.security.bcext.ECDSAContentVerifierProviderBuilder;

/**
 * @author Lijun Liao
 */

public class KeyUtil {
    private static final DefaultDigestAlgorithmIdentifierFinder dfltDigesAlgIdentifierFinder = new DefaultDigestAlgorithmIdentifierFinder();

    private static final Map<String, BcContentVerifierProviderBuilder> verifierProviderBuilders = new HashMap<>();

    private static final Map<String, KeyFactory> keyFactories = new HashMap<>();

    public static ContentVerifierProvider getContentVerifierProvider(final PublicKey publicKey)
            throws OperatorCreationException, InvalidKeyException {
        String keyAlg = publicKey.getAlgorithm().toUpperCase();
        if (keyAlg.equals("EC")) {
            keyAlg = "ECDSA";
        }

        BcContentVerifierProviderBuilder builder = verifierProviderBuilders.get(keyAlg);
        if (builder == null) {
            if ("RSA".equals(keyAlg)) {
                builder = new BcRSAContentVerifierProviderBuilder(dfltDigesAlgIdentifierFinder);
            } else if ("DSA".equals(keyAlg)) {
                builder = new BcDSAContentVerifierProviderBuilder(dfltDigesAlgIdentifierFinder);
            } else if ("ECDSA".equals(keyAlg)) {
                builder = new ECDSAContentVerifierProviderBuilder(dfltDigesAlgIdentifierFinder);
            } else {
                throw new OperatorCreationException("unknown key algorithm of the public key " + keyAlg);
            }
            verifierProviderBuilders.put(keyAlg, builder);
        }

        AsymmetricKeyParameter keyParam = KeyUtil.generatePublicKeyParameter(publicKey);
        return builder.build(keyParam);
    }

    public static KeyPair generateRSAKeypair(final int keysize) throws Exception {
        return generateRSAKeypair(keysize, null);
    }

    public static KeyPair generateRSAKeypair(final int keysize, BigInteger publicExponent) throws Exception {
        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");

        if (publicExponent == null) {
            publicExponent = RSAKeyGenParameterSpec.F4;
        }
        AlgorithmParameterSpec params = new RSAKeyGenParameterSpec(keysize, publicExponent);
        kpGen.initialize(params);
        return kpGen.generateKeyPair();
    }

    public static KeyPair generateDSAKeypair(final int pLength, final int qLength) throws Exception {
        return generateDSAKeypair(pLength, qLength, 80);
    }

    public static KeyPair generateDSAKeypair(final int pLength, final int qLength, final int certainty)
            throws Exception {
        DSAParametersGenerator paramGen = new DSAParametersGenerator(new SHA512Digest());
        DSAParameterGenerationParameters genParams = new DSAParameterGenerationParameters(pLength, qLength,
                certainty, new SecureRandom());
        paramGen.init(genParams);
        DSAParameters dsaParams = paramGen.generateParameters();

        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("DSA", "BC");
        DSAParameterSpec dsaParamSpec = new DSAParameterSpec(dsaParams.getP(), dsaParams.getQ(), dsaParams.getG());
        kpGen.initialize(dsaParamSpec, new SecureRandom());
        return kpGen.generateKeyPair();
    }

    public static KeyPair generateECKeypair(final ASN1ObjectIdentifier curveId) throws Exception {
        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("ECDSA", "BC");
        ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec(curveId.getId());
        kpGen.initialize(spec);
        return kpGen.generateKeyPair();
    }

    private static KeyFactory getKeyFactory(final String algorithm) throws InvalidKeySpecException {
        synchronized (keyFactories) {
            KeyFactory kf = keyFactories.get(algorithm);
            if (kf != null) {
                return kf;
            }

            try {
                kf = KeyFactory.getInstance(algorithm, "BC");
            } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
                throw new InvalidKeySpecException(
                        "could not find KeyFactory for " + algorithm + ": " + e.getMessage());
            }
            keyFactories.put(algorithm, kf);
            return kf;
        }
    }

    public static PublicKey generatePublicKey(final SubjectPublicKeyInfo pkInfo)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        X509EncodedKeySpec keyspec;
        try {
            keyspec = new X509EncodedKeySpec(pkInfo.getEncoded());
        } catch (IOException e) {
            throw new InvalidKeySpecException(e.getMessage(), e);
        }
        ASN1ObjectIdentifier aid = pkInfo.getAlgorithm().getAlgorithm();

        KeyFactory kf;
        if (PKCSObjectIdentifiers.rsaEncryption.equals(aid)) {
            kf = KeyFactory.getInstance("RSA");
        } else if (X9ObjectIdentifiers.id_dsa.equals(aid)) {
            kf = KeyFactory.getInstance("DSA");
        } else if (X9ObjectIdentifiers.id_ecPublicKey.equals(aid)) {
            kf = KeyFactory.getInstance("ECDSA");
        } else {
            throw new InvalidKeySpecException("unsupported key algorithm: " + aid);
        }

        return kf.generatePublic(keyspec);
    }

    public static RSAPublicKey generateRSAPublicKey(final BigInteger modulus, final BigInteger publicExponent)
            throws InvalidKeySpecException {
        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);
        KeyFactory kf = getKeyFactory("RSA");
        return (RSAPublicKey) kf.generatePublic(keySpec);
    }

    public static ECPublicKey generateECPublicKey(final String curveOid, final byte[] encodedQ)
            throws InvalidKeySpecException {
        ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec(curveOid);
        ECPoint q = spec.getCurve().decodePoint(encodedQ);
        ECPublicKeySpec keySpec = new ECPublicKeySpec(q, spec);

        KeyFactory kf = getKeyFactory("EC");
        return (ECPublicKey) kf.generatePublic(keySpec);
    }

    public static AsymmetricKeyParameter generatePrivateKeyParameter(final PrivateKey key)
            throws InvalidKeyException {
        if (key instanceof RSAPrivateCrtKey) {
            RSAPrivateCrtKey k = (RSAPrivateCrtKey) key;

            return new RSAPrivateCrtKeyParameters(k.getModulus(), k.getPublicExponent(), k.getPrivateExponent(),
                    k.getPrimeP(), k.getPrimeQ(), k.getPrimeExponentP(), k.getPrimeExponentQ(),
                    k.getCrtCoefficient());
        } else if (key instanceof RSAPrivateKey) {
            RSAPrivateKey k = (RSAPrivateKey) key;

            return new RSAKeyParameters(true, k.getModulus(), k.getPrivateExponent());
        } else if (key instanceof ECPrivateKey) {
            return ECUtil.generatePrivateKeyParameter(key);
        } else if (key instanceof DSAPrivateKey) {
            return DSAUtil.generatePrivateKeyParameter(key);
        } else {
            throw new InvalidKeyException("unknown key " + key.getClass().getName());
        }
    }

    public static AsymmetricKeyParameter generatePublicKeyParameter(final PublicKey key)
            throws InvalidKeyException {
        if (key instanceof RSAPublicKey) {
            RSAPublicKey k = (RSAPublicKey) key;
            return new RSAKeyParameters(false, k.getModulus(), k.getPublicExponent());
        } else if (key instanceof ECPublicKey) {
            return ECUtil.generatePublicKeyParameter(key);
        } else if (key instanceof DSAPublicKey) {
            return DSAUtil.generatePublicKeyParameter(key);
        } else {
            throw new InvalidKeyException("unknown key " + key.getClass().getName());
        }
    }

    /**
     * Create a SubjectPublicKeyInfo public key.
     *
     * @param publicKey the SubjectPublicKeyInfo encoding
     * @return the appropriate key parameter
     * @throws java.io.IOException on an error encoding the key
     */
    public static SubjectPublicKeyInfo creatDSASubjectPublicKeyInfo(final DSAPublicKey publicKey)
            throws IOException {
        ASN1EncodableVector v = new ASN1EncodableVector();
        v.add(new ASN1Integer(publicKey.getParams().getP()));
        v.add(new ASN1Integer(publicKey.getParams().getQ()));
        v.add(new ASN1Integer(publicKey.getParams().getG()));
        ASN1Sequence dssParams = new DERSequence(v);

        return new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, dssParams),
                new ASN1Integer(publicKey.getY()));
    }

}