io.apigee.trireme.crypto.algorithms.DsaKeyPairProvider.java Source code

Java tutorial

Introduction

Here is the source code for io.apigee.trireme.crypto.algorithms.DsaKeyPairProvider.java

Source

/**
 * Copyright 2014 Apigee Corporation.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package io.apigee.trireme.crypto.algorithms;

import io.apigee.trireme.kernel.crypto.CryptoException;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.Reader;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.DSAPublicKeySpec;

public class DsaKeyPairProvider extends KeyPairProvider {
    private static final Logger log = LoggerFactory.getLogger(DsaKeyPairProvider.class);

    public static final String DSA_TYPE = "DSA PRIVATE KEY";

    @Override
    public boolean isSupported(String algorithm) {
        return "DSA".equals(algorithm);
    }

    /**
     * DSA Key Pair format -- the PEM file contains an ASN.1 sequence containing six integers:
     * p, q, g, y, and x. We construct the appropriate Java data structures after parsing those.
     */
    @Override
    public KeyPair readKeyPair(String algorithm, Reader rdr, char[] passphrase)
            throws CryptoException, IOException {
        PemReader reader = new PemReader(rdr);

        PemObject pemObj = reader.readPemObject();
        if (pemObj == null) {
            throw new CryptoException("Not a valid PEM file");
        }

        if (!DSA_TYPE.equals(pemObj.getType())) {
            throw new CryptoException("PEM file does not contain a DSA private key");
        }

        ASN1InputStream asnIn = new ASN1InputStream(pemObj.getContent());
        ASN1Primitive ao = asnIn.readObject();
        if (ao == null) {
            throw new CryptoException("PEM file does not contain an ASN.1 object");
        }
        if (!(ao instanceof ASN1Sequence)) {
            throw new CryptoException("PEM file does not contain a sequence");
        }

        ASN1Sequence seq = (ASN1Sequence) ao;
        if (seq.size() != 6) {
            throw new CryptoException("ASN.1 sequence is the wrong length for a DSA key");
        }

        DERInteger p = (DERInteger) seq.getObjectAt(1);
        DERInteger q = (DERInteger) seq.getObjectAt(2);
        DERInteger g = (DERInteger) seq.getObjectAt(3);
        DERInteger y = (DERInteger) seq.getObjectAt(4);
        DERInteger x = (DERInteger) seq.getObjectAt(5);

        try {
            KeyFactory factory = KeyFactory.getInstance("DSA");

            DSAPublicKeySpec pubSpec = new DSAPublicKeySpec(y.getValue(), p.getValue(), q.getValue(), g.getValue());
            PublicKey pub = factory.generatePublic(pubSpec);

            DSAPrivateKeySpec keySpec = new DSAPrivateKeySpec(x.getValue(), p.getValue(), q.getValue(),
                    g.getValue());
            PrivateKey key = factory.generatePrivate(keySpec);

            return new KeyPair(pub, key);

        } catch (GeneralSecurityException gse) {
            throw new CryptoException(gse);
        }
    }

    /**
     * DSA public key format -- the PEM file contains a "SubjectPublicKeyInfo" object, which contains
     * an "Algorithm Identifier" that consists of three integers (p, q, and g) and a single
     * integer representing y. We use those four parts to assemble a Java public key.
     */
    @Override
    public PublicKey readPublicKey(String algorithm, Reader rdr) throws CryptoException, IOException {
        PEMParser pp = new PEMParser(rdr);
        try {
            Object po = pp.readObject();
            if (log.isDebugEnabled()) {
                log.debug("Trying to read an {} public key and got {}", algorithm, po);
            }

            if (po instanceof SubjectPublicKeyInfo) {
                SubjectPublicKeyInfo pk = (SubjectPublicKeyInfo) po;

                AlgorithmIdentifier alg = pk.getAlgorithm();
                if (!(alg.getParameters() instanceof ASN1Sequence)) {
                    throw new CryptoException("Invalid DSA public key format: Algorithm ID not a Sequence");
                }

                ASN1Sequence identifiers = (ASN1Sequence) (alg.getParameters());
                if (identifiers.size() != 3) {
                    throw new CryptoException("Invalid DSA public key format: Identifier does not have 3 items");
                }

                DERInteger p = (DERInteger) identifiers.getObjectAt(0);
                DERInteger q = (DERInteger) identifiers.getObjectAt(1);
                DERInteger g = (DERInteger) identifiers.getObjectAt(2);

                ASN1Primitive pkPrim = pk.parsePublicKey();
                if (!(pkPrim instanceof ASN1Integer)) {
                    throw new CryptoException("Invalid DSA public key format: Public key is not an integer");
                }
                DERInteger y = (DERInteger) pkPrim;

                try {
                    KeyFactory factory = KeyFactory.getInstance("DSA");
                    DSAPublicKeySpec pubSpec = new DSAPublicKeySpec(y.getValue(), p.getValue(), q.getValue(),
                            g.getValue());
                    return factory.generatePublic(pubSpec);
                } catch (GeneralSecurityException gse) {
                    throw new CryptoException(gse);
                }
            }
            throw new CryptoException("Input data does not contain a public key");
        } finally {
            pp.close();
        }
    }
}