org.opentestsystem.delivery.testreg.transformer.GpgVerifier.java Source code

Java tutorial

Introduction

Here is the source code for org.opentestsystem.delivery.testreg.transformer.GpgVerifier.java

Source

/*
Educational Online Test Delivery System Copyright (c) 2013 American Institutes for Research
    
Distributed under the AIR Open Source License, Version 1.0 See accompanying file AIR-License-1_0.txt or at
http://www.smarterapp.org/documents/American_Institutes_for_Research_Open_Source_Software_License.pdf
 */

package org.opentestsystem.delivery.testreg.transformer;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.SignatureException;
import java.util.Iterator;

import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPOnePassSignature;
import org.bouncycastle.openpgp.PGPOnePassSignatureList;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureList;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.bouncycastle.util.io.Streams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * Test helper class used to unencrypt and verify signature for an encrypted stream
 * 
 */
@Component
public class GpgVerifier {

    @Autowired
    private GPGEncryptor encryptor;

    @Value("${gpgKeyring.secret.location}")
    private String secretKeyringLocation;

    @Value("${gpgKeyring.public.location}")
    private String publicKeyringLocation;

    // secret passphrase for the landingzone test secret key
    private static final char[] LANDINGZONE_PASS = "testlandingzone".toCharArray();

    private static final Logger LOGGER = LoggerFactory.getLogger(GpgVerifier.class);

    public byte[] decryptAndVerify(File encryptedSignedFile) throws IOException, SignatureException, PGPException {

        byte[] output = null;

        InputStream in = PGPUtil.getDecoderStream(new FileInputStream(encryptedSignedFile));
        InputStream publicKeyIn = encryptor.getStreamForPath(publicKeyringLocation);

        ByteArrayOutputStream fOut = new ByteArrayOutputStream();

        PGPObjectFactory pgpF = new PGPObjectFactory(in);
        PGPEncryptedDataList enc;

        Object o = pgpF.nextObject();
        //
        // the first object might be a PGP marker packet.
        //

        while (!(o instanceof PGPEncryptedDataList)) {
            o = pgpF.nextObject();
        }

        if (o instanceof PGPEncryptedDataList) {
            enc = (PGPEncryptedDataList) o;
        } else {
            enc = (PGPEncryptedDataList) pgpF.nextObject();
        }

        //
        // find the secret key
        //
        Iterator<?> it = enc.getEncryptedDataObjects();
        PGPPrivateKey sKey = null;
        PGPPublicKeyEncryptedData pbe = null;

        while (sKey == null && it.hasNext()) {
            pbe = (PGPPublicKeyEncryptedData) it.next();
            InputStream secretKeyringInputStream = encryptor.getStreamForPath(secretKeyringLocation);

            PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
                    PGPUtil.getDecoderStream(secretKeyringInputStream));
            PGPSecretKey pgpSecKey = pgpSec.getSecretKey(pbe.getKeyID());
            if (pgpSecKey == null) {
                fail("could not find secret key");
            }

            PBESecretKeyDecryptor decryptor = new BcPBESecretKeyDecryptorBuilder(
                    new BcPGPDigestCalculatorProvider()).build(LANDINGZONE_PASS);

            sKey = pgpSecKey.extractPrivateKey(decryptor);
        }

        if (sKey == null) {
            throw new IllegalArgumentException("secret key for message not found.");
        }

        InputStream clear = pbe
                .getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("BC").build(sKey));

        PGPObjectFactory plainFact = new PGPObjectFactory(clear);

        Object message = null;

        PGPOnePassSignatureList onePassSignatureList = null;
        PGPSignatureList signatureList = null;
        PGPCompressedData compressedData = null;

        message = plainFact.nextObject();
        ByteArrayOutputStream actualOutput = new ByteArrayOutputStream();

        while (message != null) {
            LOGGER.debug("decrypted message: " + message.toString());
            if (message instanceof PGPCompressedData) {
                compressedData = (PGPCompressedData) message;
                plainFact = new PGPObjectFactory(compressedData.getDataStream());
                message = plainFact.nextObject();
            }

            if (message instanceof PGPLiteralData) {
                // have to read it and keep it somewhere.
                Streams.pipeAll(((PGPLiteralData) message).getInputStream(), actualOutput);
            } else if (message instanceof PGPOnePassSignatureList) {
                onePassSignatureList = (PGPOnePassSignatureList) message;
            } else if (message instanceof PGPSignatureList) {
                signatureList = (PGPSignatureList) message;
            } else {
                throw new PGPException("message unknown message type.");
            }
            message = plainFact.nextObject();
        }
        actualOutput.close();
        PGPPublicKey publicKey = null;
        output = actualOutput.toByteArray();

        if (onePassSignatureList == null || signatureList == null) {
            throw new PGPException("Signatures not found.");
        } else {

            for (int i = 0; i < onePassSignatureList.size(); i++) {
                PGPOnePassSignature ops = onePassSignatureList.get(0);
                LOGGER.debug("verifier : " + ops.getKeyID());
                PGPPublicKeyRingCollection pgpRing = new PGPPublicKeyRingCollection(
                        PGPUtil.getDecoderStream(publicKeyIn));
                publicKey = pgpRing.getPublicKey(ops.getKeyID());
                if (publicKey != null) {
                    ops.init(new JcaPGPContentVerifierBuilderProvider().setProvider("BC"), publicKey);
                    ops.update(output);
                    PGPSignature signature = signatureList.get(i);
                    // apparently the signature can only be verified once?? if the verify method is called a 2nd time it
                    // will fail
                    boolean signatureVerified = ops.verify(signature);
                    assertThat(signatureVerified, is(true));
                    if (signatureVerified) {
                        Iterator<?> userIds = publicKey.getUserIDs();
                        while (userIds.hasNext()) {
                            String userId = (String) userIds.next();
                            LOGGER.debug("Signed by " + userId);
                        }
                        LOGGER.debug("Signature verified");
                    } else {
                        throw new SignatureException("Signature verification failed");
                    }
                }
            }

        }

        if (pbe.isIntegrityProtected() && !pbe.verify()) {
            throw new PGPException("Data is integrity protected but integrity is lost.");
        } else if (publicKey == null) {
            throw new SignatureException("Signature not found");
        } else {
            fOut.write(output);
            fOut.flush();
            fOut.close();

            LOGGER.debug("decrypt and verify output: " + fOut.toString());
        }

        return output;
    }

}