[prev in list] [next in list] [prev in thread] [next in thread] 

List:       bouncycastle-crypto-dev
Subject:    Re: [dev-crypto] Creating a Separate Signature File
From:       David Hook <dgh () lockboxlabs ! com>
Date:       2008-06-25 3:58:21
Message-ID: 1214366301.3633.25.camel () echidna
[Download RAW message or body]

Yes, sign then encrypt, the motto is "always sign what you mean".

An example of this for PGP (which is what I'm assuming you are using)
was posted a while a go. I've attached it to the email as an example -
for some reason the original message didn't get archived. Basically it's
just a matter of replacing the code supporting the literal data object
with code supporting a signed one.

Regards,

David

On Tue, 2008-06-24 at 18:43 -0400, Mark Lookabaugh wrote:
> Hi,
>  
> We have binary plaintext data which we would like to sign and
> encrypt.  
>  
> My understanding is that we should first sign the plaintext data
> creating a separate signature file, next encrypt the plaintext data,
> and finally forward the encrypted data and the signature file together
> to the recipient.   Does that sound like the proper procedure?
>  
> We have the encryption code in place already.  What is the process to
> create a signature file that is separate from the item being signed?
>  
> Thank you,
> Mark
>  

["EncryptDecryptLargeFiles.java" (EncryptDecryptLargeFiles.java)]

package myTest;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.SignatureException;
import java.util.Date;
import java.util.Iterator;

import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
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.PGPLiteralDataGenerator;
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.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPSignatureList;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.PGPUtil;

public class EncryptDecryptLargeFiles {

    private static PGPPublicKey readPublicKey(
        InputStream    in)
        throws IOException, PGPException
    {
        in = PGPUtil.getDecoderStream(in);
        
        PGPPublicKeyRingCollection        pgpPub = new \
PGPPublicKeyRingCollection(in);

        //
        // we just loop through the collection till we find a key suitable for \
encryption, in the real  // world you would probably want to be a bit smarter about \
this.  //
        PGPPublicKey    key = null;
        
        //
        // iterate through the key rings.
        //
        Iterator rIt = pgpPub.getKeyRings();
        
        while (key == null && rIt.hasNext())
        {
            PGPPublicKeyRing    kRing = (PGPPublicKeyRing)rIt.next();    
            Iterator            kIt = kRing.getPublicKeys();
            
            while (key == null && kIt.hasNext())
            {
                PGPPublicKey    k = (PGPPublicKey)kIt.next();
                
                if (k.isEncryptionKey())
                {
                    key = k;
                }
            }
        }
        
        if (key == null)
        {
            throw new IllegalArgumentException("Can't find encryption key in key \
ring.");  }
        
        return key;
    }
    
    private static PGPPrivateKey findSecretKey(
        PGPSecretKeyRingCollection  pgpSec,
        long                        keyID,
        char[]                      pass)
        throws PGPException, NoSuchProviderException
    {    
        PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
        
        if (pgpSecKey == null)
        {
            return null;
        }
        
        return pgpSecKey.extractPrivateKey(pass, "BC");
    }
    
        /**
	 * To Decrypt and then verify signature
	 */
	public static void decryptThenVerifySign(InputStream in, InputStream keyIn, char[] \
passwd, InputStream pubKeyIn) throws Exception{  in = PGPUtil.getDecoderStream(in);
		PGPObjectFactory pgpF = new PGPObjectFactory(in);
        PGPEncryptedDataList    enc;
        Object                  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;
        PGPSecretKeyRingCollection  pgpSec = new \
PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyIn));  while (sKey == null && \
it.hasNext())  {
            pbe = (PGPPublicKeyEncryptedData)it.next();
            sKey = findSecretKey(pgpSec, pbe.getKeyID(), passwd);
            System.out.println("Secret Key ID : " + sKey.getKeyID());
        }
        
        if (sKey == null)
        {
            throw new IllegalArgumentException("secret key for message not found.");
        }
        // decrypt the data
        InputStream         clear = pbe.getDataStream(sKey, "BC");
        PGPObjectFactory    plainFact = new PGPObjectFactory(clear);
        Object              message = plainFact.nextObject();
        System.out.println("Object is " + message);
        if (message instanceof PGPCompressedData)
        {
        	 System.out.println("Found instance of PGPCompressedData");
        	 PGPCompressedData   cData = (PGPCompressedData)message;
        	 PGPObjectFactory    pgpFact = new PGPObjectFactory(cData.getDataStream());
             message = pgpFact.nextObject();
             System.out.println("Object is " + message);
             
             if (message instanceof PGPOnePassSignatureList)
             {
            	 System.out.println("Found instance of PGPOnePassSignatureList");
            	 // verify the signature
            	 PGPOnePassSignature ops = ((PGPOnePassSignatureList) message).get(0);
            	 PGPPublicKeyRingCollection  pgpRing = new \
                PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(pubKeyIn));
                 PGPPublicKey                key = \
pgpRing.getPublicKey(ops.getKeyID());  System.out.println("Public Key ID : " + \
key.getKeyID());  
                 ops.initVerify(key, "BC");
                 
                 message = pgpFact.nextObject();
                 if (message instanceof PGPLiteralData) {
                	 System.out.println("Found instance of PGPLiteralData");
                	 
                	 PGPLiteralData       ld = (PGPLiteralData)message;
                	 FileOutputStream     fOut = new \
                FileOutputStream("sample.txt.decrypt");
                	 BufferedOutputStream bOut = new BufferedOutputStream(fOut);
                	 
                	 
                     /** use of buffering to speed up write **/
                     
                     byte[] buffer = new byte[1<<16];
          	         InputStream fIn = ld.getInputStream();
          	        
         	         int bytesRead = 0;
         	         while((bytesRead = fIn.read(buffer)) != -1) {
         	        	bOut.write(buffer,0,bytesRead);
         	        	ops.update(buffer,0,bytesRead);                        
         	         }
         	        
         	          bOut.close();
         	          
         	          
                     // verify the signature
         			 if (ops != null) {
         				PGPSignatureList signatureList = (PGPSignatureList) \
                pgpFact.nextObject();
         				System.out.println("signature list (" + signatureList.size() + " sigs) \
                is " + signatureList);
         				PGPSignature messageSignature = (PGPSignature) signatureList.get(0);
         				System.out.println("verification signature is " + messageSignature);
         				if (!ops.verify(messageSignature)) {
         					throw new RuntimeException("signature verification failed");
         				}
         				else{
         					System.out.println(" *** Signature verification success *** ");
         				}
         			}
                	 
                 }// end of if PGPLiteralData
             }// end of if PGPOnePassSignatureList
        }
        else{
        	System.err.println("PGPCompressedData not available!");
        }
        
	}
	
    
    /**
     * Method to sign-THEN-encrypt
     */
    private static void signThenEncryptFile(
            OutputStream    out,
            String          fileName,
            PGPPublicKey    encKey,
            boolean         armor,
            boolean         withIntegrityCheck,
            InputStream keyIn,
            char[] pass)
            throws IOException, NoSuchAlgorithmException, NoSuchProviderException, \
PGPException, SignatureException  {    
            if (armor)
            {
                out = new ArmoredOutputStream(out);
            }
            
            try
            {    
                PGPEncryptedDataGenerator   cPk = new \
PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, withIntegrityCheck, new \
SecureRandom(), "BC");  cPk.addMethod(encKey);
                
                OutputStream                cOut = cPk.open(out, new byte[1 << 16]);
                
                //PGPCompressedDataGenerator  comData = new \
                PGPCompressedDataGenerator(PGPCompressedData.ZIP);
                //PGPUtil.writeFileToLiteralData(comData.open(cOut), \
PGPLiteralData.BINARY, new File(fileName), new byte[1 << 16]);  
                ///// instead of the above two commented line include the code from \
SignedFileProcessor.java  PGPSecretKey                pgpSec = readSecretKey(keyIn);
                PGPPrivateKey               pgpPrivKey = \
                pgpSec.extractPrivateKey(pass, "BC");        
                PGPSignatureGenerator       sGen = new \
PGPSignatureGenerator(pgpSec.getPublicKey().getAlgorithm(), PGPUtil.SHA1, "BC");  
                sGen.initSign(PGPSignature.BINARY_DOCUMENT, pgpPrivKey);
                
                Iterator    it = pgpSec.getPublicKey().getUserIDs();
                if (it.hasNext())
                {
                    PGPSignatureSubpacketGenerator  spGen = new \
PGPSignatureSubpacketGenerator();  
                    spGen.setSignerUserID(false, (String)it.next());
                    sGen.setHashedSubpackets(spGen.generate());
                }
                
                PGPCompressedDataGenerator  cGen = new PGPCompressedDataGenerator(
                                                                        \
                PGPCompressedData.ZLIB);
                BCPGOutputStream            bOut = new \
BCPGOutputStream(cGen.open(cOut));  sGen.generateOnePassVersion(false).encode(bOut);
                
                File                        file = new File(fileName);
                PGPLiteralDataGenerator     lGen = new PGPLiteralDataGenerator();
                OutputStream                lOut = lGen.open(bOut, \
PGPLiteralData.BINARY, file.getName(), new Date(),new byte[1<<16]);  // use of \
buffering to speed up write  byte[] buffer = new byte[1<<16];
     	        FileInputStream fIn = new FileInputStream(file);
     	        
    	        int bytesRead = 0;
    	        while((bytesRead = fIn.read(buffer)) != -1) {
    	        	lOut.write(buffer,0,bytesRead);
    	        	sGen.update(buffer,0,bytesRead);                        
    	        	lOut.flush();
    	        }

                lGen.close();
                sGen.generate().encode(bOut);
                cGen.close();
                /////end of sign
                
                //comData.close();
                cOut.close();
                out.close();
            }
            catch (PGPException e)
            {
                System.err.println(e);
                if (e.getUnderlyingException() != null)
                {
                    e.getUnderlyingException().printStackTrace();
                }
            }
        }
    
    /**
     * A simple routine that opens a key ring file and loads the first available key \
                suitable for
     * signature generation.
     * 
     * @param in
     * @return
     * @throws IOException
     * @throws PGPException
     */
    private static PGPSecretKey readSecretKey(
        InputStream    in)
        throws IOException, PGPException
    {
        in = PGPUtil.getDecoderStream(in);
        
        PGPSecretKeyRingCollection        pgpSec = new \
PGPSecretKeyRingCollection(in);

        //
        // we just loop through the collection till we find a key suitable for \
encryption, in the real  // world you would probably want to be a bit smarter about \
this.  //
        PGPSecretKey    key = null;
        
        //
        // iterate through the key rings.
        //
        Iterator rIt = pgpSec.getKeyRings();
        
        while (key == null && rIt.hasNext())
        {
            PGPSecretKeyRing    kRing = (PGPSecretKeyRing)rIt.next();    
            Iterator            kIt = kRing.getSecretKeys();
            
            while (key == null && kIt.hasNext())
            {
                PGPSecretKey    k = (PGPSecretKey)kIt.next();
                
                if (k.isSigningKey())
                {
                    key = k;
                }
            }
        }
        
        if (key == null)
        {
            throw new IllegalArgumentException("Can't find signing key in key \
ring.");  }
        
        return key;
    }
    
    
    private static String inputFileName = "sample.txt";
	private static String outputFileName = inputFileName + ".asc";
	
	static String publicKeyFilePath = \
"C:/CustomerProjects/PGP/Testing/Source/conf/pubring.pkr";  static String \
privateKeyFilePath = "C:/CustomerProjects/PGP/Testing/Source/conf/secring.skr";  
	static String password = "abc";
	
	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception{
		Security.addProvider(new BouncyCastleProvider());
		System.out.println(">>> This is test for LARGE files");
		
		// to test digitally signing/verifying  LARGE files
		System.out.println(">>> START - sign-THEN-encrypt");
		// public key 
		FileInputStream     publicKeyIn = new FileInputStream(publicKeyFilePath);
		// private key
		FileInputStream     privKeyIn = new FileInputStream(privateKeyFilePath);
		// output file name (sample.txt.asc)
		FileOutputStream fOut = new FileOutputStream(outputFileName);
		// sign-THEN-encrypt
		signThenEncryptFile(fOut, inputFileName, readPublicKey(publicKeyIn), true, true, \
privKeyIn, password.toCharArray());  System.out.println(">>> END -  \
sign-THEN-encrypt");  
		
		System.out.println(">>> START - decrypt-THEN-verify");
		// encrypted file name (sample.txt.asc)
		FileInputStream fin1 = new FileInputStream(outputFileName);
		// private key
		FileInputStream fprivate1= new FileInputStream(privateKeyFilePath);
		// public key
		FileInputStream fpublic1 = new FileInputStream(publicKeyFilePath);
		// decrypt-THEN-verify
		decryptThenVerifySign(fin1, fprivate1, password.toCharArray(),fpublic1);
		System.out.println(">>> END - decrypt-THEN-verify");
		
	}
}



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic