/* * $Id$ * * Authors: * Jeff Buchbinder <jeff@freemedsoftware.org> * Example code from Bouncy Castle -- thanks guys! * * REMITT Electronic Medical Information Translation and Transmission * Copyright (C) 1999-2014 FreeMED Software Foundation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.remitt.server; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.NoSuchProviderException; import java.security.SecureRandom; import java.util.Iterator; import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.openpgp.PGPCompressedData; import org.bouncycastle.openpgp.PGPCompressedDataGenerator; import org.bouncycastle.openpgp.PGPEncryptedData; import org.bouncycastle.openpgp.PGPEncryptedDataGenerator; import org.bouncycastle.openpgp.PGPEncryptedDataList; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPLiteralData; import org.bouncycastle.openpgp.PGPObjectFactory; import org.bouncycastle.openpgp.PGPOnePassSignatureList; import org.bouncycastle.openpgp.PGPPrivateKey; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPUtil; public class PGPProvider { static final Logger log = Logger.getLogger(PGPProvider.class); /** * * @param keyname * @return * @throws IOException */ public static InputStream getKeyFromFile(String keyname) throws IOException { return FileUtils.openInputStream(new File(Configuration .getServletContext().getServletContext().getRealPath( "/WEB-INF/keys/" + keyname + ".asc"))); } /** * * @param pgpSec * @param keyID * @param pass * @return * @throws PGPException * @throws NoSuchProviderException */ private static PGPPrivateKey findSecretKey( PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass) throws PGPException, NoSuchProviderException { PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID); return pgpSecKey == null ? null : pgpSecKey.extractPrivateKey(pass, "BC"); } /** * * @param keyname * @return * @throws IOException * @throws PGPException */ @SuppressWarnings("unchecked") private static PGPPublicKey readPublicKey(String keyname) throws IOException, PGPException { InputStream in = PGPUtil.getDecoderStream(getKeyFromFile(keyname)); PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in); Iterator<PGPPublicKeyRing> rIt = pgpPub.getKeyRings(); while (rIt.hasNext()) { PGPPublicKeyRing kRing = rIt.next(); Iterator<PGPPublicKey> kIt = kRing.getPublicKeys(); while (kIt.hasNext()) { PGPPublicKey k = kIt.next(); if (k.isEncryptionKey()) { return k; } } } throw new IllegalArgumentException("No key found in ring"); } /** * * @param in * @param keyIn * @param password * @return * @throws IOException * @throws NoSuchProviderException */ public static byte[] decryptMessage(InputStream in, InputStream keyIn, char[] password) throws IOException, NoSuchProviderException { in = PGPUtil.getDecoderStream(in); try { PGPObjectFactory pgpF = new PGPObjectFactory(in); PGPEncryptedDataList enc = null; Object o = pgpF.nextObject(); if (o instanceof PGPEncryptedDataList) { enc = (PGPEncryptedDataList) o; } else { enc = (PGPEncryptedDataList) pgpF.nextObject(); } Iterator it = enc.getEncryptedDataObjects(); PGPPrivateKey sKey = null; PGPPublicKeyEncryptedData pbe = null; PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection( PGPUtil.getDecoderStream(keyIn)); while (sKey == null && it.hasNext()) { pbe = (PGPPublicKeyEncryptedData) it.next(); sKey = findSecretKey(pgpSec, pbe.getKeyID(), password); } if (sKey == null) { throw new IllegalArgumentException( "Secret key for message not found."); } InputStream clear = pbe.getDataStream(sKey, "BC"); PGPObjectFactory plainFact = new PGPObjectFactory(clear); Object message = plainFact.nextObject(); if (message instanceof PGPCompressedData) { PGPCompressedData cData = (PGPCompressedData) message; PGPObjectFactory pgpFact = new PGPObjectFactory(cData .getDataStream()); message = pgpFact.nextObject(); } if (message instanceof PGPLiteralData) { PGPLiteralData ld = (PGPLiteralData) message; ByteArrayOutputStream fOut = new ByteArrayOutputStream(); InputStream unc = ld.getInputStream(); int ch; while ((ch = unc.read()) >= 0) { fOut.write(ch); } if (pbe.isIntegrityProtected()) { if (!pbe.verify()) { log.error("Message failed integrity check"); } else { log.info("Message integrity check passed"); } } else { log.trace("No message integrity check"); } fOut.flush(); fOut.close(); return fOut.toByteArray(); } else if (message instanceof PGPOnePassSignatureList) { throw new PGPException( "Encrypted message contains a signed message - not literal data."); } else { throw new PGPException( "Message is not a simple encrypted file - type unknown."); } } catch (PGPException ex) { log.error(ex); } return null; } /** * * @param data * @param encKeyName * @return * @throws IOException * @throws NoSuchProviderException * @throws PGPException */ public static byte[] encryptMessage(byte[] data, String encKeyName) throws IOException, NoSuchProviderException, PGPException { return encryptMessage(data, readPublicKey(encKeyName)); } /** * * @param data * @param encKey * @return * @throws IOException * @throws NoSuchProviderException */ public static byte[] encryptMessage(byte[] data, PGPPublicKey encKey) throws IOException, NoSuchProviderException, PGPException { boolean armor = true; boolean withIntegrityCheck = true; byte[] ret = null; OutputStream out = new ByteArrayOutputStream(); if (armor) { out = new ArmoredOutputStream(out); } File tempfile = null; try { tempfile = File.createTempFile("pgp", null); FileUtils.writeByteArrayToFile(tempfile, data); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator( PGPCompressedData.ZIP); PGPUtil.writeFileToLiteralData(comData.open(bOut), PGPLiteralData.BINARY, tempfile); comData.close(); PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator( PGPEncryptedData.CAST5, withIntegrityCheck, new SecureRandom(), "BC"); cPk.addMethod(encKey); ret = bOut.toByteArray(); } catch (PGPException e) { log.error(e); if (e.getUnderlyingException() != null) { log.error(e.getUnderlyingException()); } // Clean up if (tempfile != null) { tempfile.delete(); } throw e; } finally { // Clean up if (tempfile != null) { tempfile.delete(); } } return ret; } }