org.eclipse.andmore.android.certmanager.packaging.sign.SignatureBlockFile.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.andmore.android.certmanager.packaging.sign.SignatureBlockFile.java

Source

/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * 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 org.eclipse.andmore.android.certmanager.packaging.sign;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidKeyException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.eclipse.andmore.android.certmanager.CertificateManagerActivator;
import org.eclipse.andmore.android.certmanager.exception.KeyStoreManagerException;
import org.eclipse.andmore.android.certmanager.ui.model.IKeyStoreEntry;
import org.eclipse.andmore.android.common.log.AndmoreLogger;

/**
 * This class implements the signature block file from jar mechanism used in
 * packaging
 */
public class SignatureBlockFile {

    /**
     * the signature file
     */
    private SignatureFile signatureFile;

    /**
     * A certificate from keystore
     */
    private IKeyStoreEntry keystoreEntry;

    /**
     * The password of the certificate.
     */
    private String keyEntryPassword;

    /**
     * Default Constructor
     * 
     * @param signatureFile
     *            the signature file
     * @param alias
     *            the certificate alias
     */
    public SignatureBlockFile(SignatureFile signatureFile, IKeyStoreEntry keystoreEntry, String keyEntryPassword) {
        this.keyEntryPassword = keyEntryPassword;
        this.keystoreEntry = keystoreEntry;
        this.signatureFile = signatureFile;
    }

    /**
     * To string method override
     * 
     * @return the signature block file name with relative path from root.
     *         Frequently META-INF/alias.RSA or .DSA
     */
    @Override
    public String toString() {
        String result = new String();
        try {
            result = new StringBuilder(CertificateManagerActivator.METAFILES_DIR)
                    .append(CertificateManagerActivator.JAR_SEPARATOR).append(ISignConstants.SIGNATURE_FILE_NAME)
                    .append(".").append(getBlockAlgorithm()).toString();
        } catch (UnrecoverableKeyException e) {
            AndmoreLogger.error("Could not generate signature block file name.");
        } catch (KeyStoreException e) {
            AndmoreLogger.error("Could not generate signature block file name.");
        } catch (NoSuchAlgorithmException e) {
            AndmoreLogger.error("Could not generate signature block file name.");
        } catch (KeyStoreManagerException e) {
            AndmoreLogger.error("Could not generate signature block file name.");
        }

        return result;
    }

    /**
     * Gets the block file algorithm
     * 
     * @return the signature block file algorithm to be used
     * @throws KeyStoreManagerException
     * @throws NoSuchAlgorithmException
     * @throws KeyStoreException
     * @throws UnrecoverableKeyException
     * @throws
     */
    private String getBlockAlgorithm() throws UnrecoverableKeyException, KeyStoreException,
            NoSuchAlgorithmException, KeyStoreManagerException {
        return keystoreEntry.getKey(this.keyEntryPassword).getAlgorithm();
    }

    /**
     * Writes this file to an output stream
     * 
     * @param outputStream
     *            the output stream to write the file
     * @throws IOException
     *             if an I/O error occurs during the signing process
     * @throws SignException
     *             if a processing error occurs during the signing process
     * @throws KeyStoreManagerException
     * @throws KeyStoreException
     * @throws UnrecoverableKeyException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException 
     * @throws CertificateEncodingException 
     * @throws OperatorCreationException 
     * @throws CMSException 
     */
    public void write(OutputStream outputStream) throws IOException, SignException, UnrecoverableKeyException,
            KeyStoreException, KeyStoreManagerException, NoSuchAlgorithmException, InvalidKeyException,
            CertificateEncodingException, OperatorCreationException, CMSException {
        // get certificate from entry
        X509Certificate[] certChain = { keystoreEntry.getX509Certificate() };
        if (certChain.length > 0) {
            X509Certificate publicKey = certChain[0];
            PrivateKey privateKey = keystoreEntry.getPrivateKey(keyEntryPassword);
            String blockalgorithm = getBlockAlgorithm();
            if (!blockalgorithm.equalsIgnoreCase(ISignConstants.DSA)
                    && !blockalgorithm.equalsIgnoreCase(ISignConstants.RSA)) {
                AndmoreLogger.error(SignatureBlockFile.class,
                        "Signing block algorithm not supported. Key algorithm must be DSA or RSA");
                throw new SignException("Signing block algorithm not supported");
            }

            String signatureAlgorithm = ISignConstants.SHA1 + ISignConstants.ALGORITHM_CONNECTOR + blockalgorithm;

            Security.addProvider(new BouncyCastleProvider());

            ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>();
            certList.add(publicKey);
            JcaCertStore certs = new JcaCertStore(certList);

            ContentSigner signer = new JcaContentSignerBuilder(signatureAlgorithm).build(privateKey);

            CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
            generator.addSignerInfoGenerator(
                    new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build())
                            .setDirectSignature(true).build(signer, publicKey));
            generator.addCertificates(certs);

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            signatureFile.write(baos);

            CMSTypedData cmsdata = new CMSProcessableByteArray(baos.toByteArray());
            CMSSignedData signeddata = generator.generate(cmsdata, false);

            ASN1InputStream asn1 = new ASN1InputStream(signeddata.getEncoded());
            DEROutputStream dos = new DEROutputStream(outputStream);
            dos.writeObject(asn1.readObject());
            dos.flush();
            dos.close();
            asn1.close();
        }
        AndmoreLogger.info(SignatureBlockFile.class, "Created signature block file");
    }
}