Java tutorial
/* 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; } }