All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bouncycastle.openpgp.PGPUtil Maven / Gradle / Ivy

Go to download

The Bouncy Castle Java API for handling the OpenPGP protocol. This jar contains the OpenPGP API for JDK 1.5. The APIs can be used in conjunction with a JCE/JCA provider such as the one provided with the Bouncy Castle Cryptography APIs.

There is a newer version: 1.46
Show newest version
package org.bouncycastle.openpgp;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Provider;
import java.util.Date;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.bcpg.ArmoredInputStream;
import org.bouncycastle.bcpg.HashAlgorithmTags;
import org.bouncycastle.bcpg.MPInteger;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.S2K;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.util.encoders.Base64;

/**
 * Basic utility class
 */
public class PGPUtil
    implements HashAlgorithmTags
{
    private    static String    defProvider = "BC";

    /**
     * Return the provider that will be used by factory classes in situations
     * where a provider must be determined on the fly.
     * 
     * @return String
     */
    public static String getDefaultProvider()
    {
        return defProvider;
    }
    
    /**
     * Set the provider to be used by the package when it is necessary to 
     * find one on the fly.
     * 
     * @param provider
     */
    public static void setDefaultProvider(
        String    provider)
    {
        defProvider = provider;
    }
    
    static MPInteger[] dsaSigToMpi(
        byte[] encoding) 
        throws PGPException
    {
        ASN1InputStream aIn = new ASN1InputStream(encoding);

        DERInteger i1;
        DERInteger i2;

        try
        {
            ASN1Sequence s = (ASN1Sequence)aIn.readObject();

            i1 = (DERInteger)s.getObjectAt(0);
            i2 = (DERInteger)s.getObjectAt(1);
        }
        catch (IOException e)
        {
            throw new PGPException("exception encoding signature", e);
        }

        MPInteger[] values = new MPInteger[2];
        
        values[0] = new MPInteger(i1.getValue());
        values[1] = new MPInteger(i2.getValue());
        
        return values;
    }
    
    static String getDigestName(
        int        hashAlgorithm)
        throws PGPException
    {
        switch (hashAlgorithm)
        {
        case HashAlgorithmTags.SHA1:
            return "SHA1";
        case HashAlgorithmTags.MD2:
            return "MD2";
        case HashAlgorithmTags.MD5:
            return "MD5";
        case HashAlgorithmTags.RIPEMD160:
            return "RIPEMD160";
        case HashAlgorithmTags.SHA256:
            return "SHA256";
        case HashAlgorithmTags.SHA384:
            return "SHA384";
        case HashAlgorithmTags.SHA512:
            return "SHA512";
        case HashAlgorithmTags.SHA224:
            return "SHA224";
        default:
            throw new PGPException("unknown hash algorithm tag in getDigestName: " + hashAlgorithm);
        }
    }
    
    static String getSignatureName(
        int        keyAlgorithm,
        int        hashAlgorithm)
        throws PGPException
    {
        String     encAlg;
                
        switch (keyAlgorithm)
        {
        case PublicKeyAlgorithmTags.RSA_GENERAL:
        case PublicKeyAlgorithmTags.RSA_SIGN:
            encAlg = "RSA";
            break;
        case PublicKeyAlgorithmTags.DSA:
            encAlg = "DSA";
            break;
        case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: // in some malformed cases.
        case PublicKeyAlgorithmTags.ELGAMAL_GENERAL:
            encAlg = "ElGamal";
            break;
        default:
            throw new PGPException("unknown algorithm tag in signature:" + keyAlgorithm);
        }

        return getDigestName(hashAlgorithm) + "with" + encAlg;
    }
    
    static String getSymmetricCipherName(
        int    algorithm) 
        throws PGPException
    {
        switch (algorithm)
        {
        case SymmetricKeyAlgorithmTags.NULL:
            return null;
        case SymmetricKeyAlgorithmTags.TRIPLE_DES:
            return "DESEDE";
        case SymmetricKeyAlgorithmTags.IDEA:
            return "IDEA";
        case SymmetricKeyAlgorithmTags.CAST5:
            return "CAST5";
        case SymmetricKeyAlgorithmTags.BLOWFISH:
            return "Blowfish";
        case SymmetricKeyAlgorithmTags.SAFER:
            return "SAFER";
        case SymmetricKeyAlgorithmTags.DES:
            return "DES";
        case SymmetricKeyAlgorithmTags.AES_128:
            return "AES";
        case SymmetricKeyAlgorithmTags.AES_192:
            return "AES";
        case SymmetricKeyAlgorithmTags.AES_256:
            return "AES";
        case SymmetricKeyAlgorithmTags.TWOFISH:
            return "Twofish";
        default:
            throw new PGPException("unknown symmetric algorithm: " + algorithm);
        }
    }
    
    public static SecretKey makeRandomKey(
        int             algorithm,
        SecureRandom    random) 
        throws PGPException
    {
        String    algName = null;
        int        keySize = 0;
        
        switch (algorithm)
        {
        case SymmetricKeyAlgorithmTags.TRIPLE_DES:
            keySize = 192;
            algName = "DES_EDE";
            break;
        case SymmetricKeyAlgorithmTags.IDEA:
            keySize = 128;
            algName = "IDEA";
            break;
        case SymmetricKeyAlgorithmTags.CAST5:
            keySize = 128;
            algName = "CAST5";
            break;
        case SymmetricKeyAlgorithmTags.BLOWFISH:
            keySize = 128;
            algName = "Blowfish";
            break;
        case SymmetricKeyAlgorithmTags.SAFER:
            keySize = 128;
            algName = "SAFER";
            break;
        case SymmetricKeyAlgorithmTags.DES:
            keySize = 64;
            algName = "DES";
            break;
        case SymmetricKeyAlgorithmTags.AES_128:
            keySize = 128;
            algName = "AES";
            break;
        case SymmetricKeyAlgorithmTags.AES_192:
            keySize = 192;
            algName = "AES";
            break;
        case SymmetricKeyAlgorithmTags.AES_256:
            keySize = 256;
            algName = "AES";
            break;
        case SymmetricKeyAlgorithmTags.TWOFISH:
            keySize = 256;
            algName = "Twofish";
            break;
        default:
            throw new PGPException("unknown symmetric algorithm: " + algorithm);
        }
        
        byte[]    keyBytes = new byte[(keySize + 7) / 8];
        
        random.nextBytes(keyBytes);
        
        return new SecretKeySpec(keyBytes, algName);
    }
    
    public static SecretKey makeKeyFromPassPhrase(
        int       algorithm,
        char[]    passPhrase,
        String    provider) 
        throws NoSuchProviderException, PGPException
    {
        return makeKeyFromPassPhrase(algorithm, null, passPhrase, provider);
    }
    
    public static SecretKey makeKeyFromPassPhrase(
        int     algorithm,
        S2K     s2k,
        char[]  passPhrase,
        String  provider) 
        throws PGPException, NoSuchProviderException
    {
        Provider prov = getProvider(provider);

        return makeKeyFromPassPhrase(algorithm, s2k, passPhrase, prov);
    }

    public static SecretKey makeKeyFromPassPhrase(
        int     algorithm,
        S2K     s2k,
        char[]  passPhrase,
        Provider provider)
        throws PGPException, NoSuchProviderException
    {
        String    algName = null;
        int        keySize = 0;
        
        switch (algorithm)
        {
        case SymmetricKeyAlgorithmTags.TRIPLE_DES:
            keySize = 192;
            algName = "DES_EDE";
            break;
        case SymmetricKeyAlgorithmTags.IDEA:
            keySize = 128;
            algName = "IDEA";
            break;
        case SymmetricKeyAlgorithmTags.CAST5:
            keySize = 128;
            algName = "CAST5";
            break;
        case SymmetricKeyAlgorithmTags.BLOWFISH:
            keySize = 128;
            algName = "Blowfish";
            break;
        case SymmetricKeyAlgorithmTags.SAFER:
            keySize = 128;
            algName = "SAFER";
            break;
        case SymmetricKeyAlgorithmTags.DES:
            keySize = 64;
            algName = "DES";
            break;
        case SymmetricKeyAlgorithmTags.AES_128:
            keySize = 128;
            algName = "AES";
            break;
        case SymmetricKeyAlgorithmTags.AES_192:
            keySize = 192;
            algName = "AES";
            break;
        case SymmetricKeyAlgorithmTags.AES_256:
            keySize = 256;
            algName = "AES";
            break;
        case SymmetricKeyAlgorithmTags.TWOFISH:
            keySize = 256;
            algName = "Twofish";
            break;
        default:
            throw new PGPException("unknown symmetric algorithm: " + algorithm);
        }
        
        byte[]           pBytes = new byte[passPhrase.length];
        MessageDigest    digest;
                    
        for (int i = 0; i != passPhrase.length; i++)
        {
            pBytes[i] = (byte)passPhrase[i];
        }
        
        byte[]    keyBytes = new byte[(keySize + 7) / 8];
        
        int    generatedBytes = 0;
        int    loopCount = 0;
        
        while (generatedBytes < keyBytes.length)
        {
            if (s2k != null)
            {     
                String digestName = getS2kDigestName(s2k);
                
                try
                {
                    digest = getDigestInstance(digestName, provider);
                }
                catch (NoSuchAlgorithmException e)
                {
                    throw new PGPException("can't find S2K digest", e);
                }

                for (int i = 0; i != loopCount; i++)
                {
                    digest.update((byte)0);
                }
                
                byte[]    iv = s2k.getIV();
                            
                switch (s2k.getType())
                {
                case S2K.SIMPLE:
                    digest.update(pBytes);
                    break;
                case S2K.SALTED:
                    digest.update(iv);
                    digest.update(pBytes);
                    break;
                case S2K.SALTED_AND_ITERATED:
                    long    count = s2k.getIterationCount();
                    digest.update(iv);
                    digest.update(pBytes);
        
                    count -= iv.length + pBytes.length;
                                
                    while (count > 0)
                    {
                        if (count < iv.length)
                        {
                            digest.update(iv, 0, (int)count);
                            break;
                        }
                        else
                        {
                            digest.update(iv);
                            count -= iv.length;
                        }
        
                        if (count < pBytes.length)
                        {
                            digest.update(pBytes, 0, (int)count);
                            count = 0;
                        }
                        else
                        {
                            digest.update(pBytes);
                            count -= pBytes.length;
                        }
                    }
                    break;
                default:
                    throw new PGPException("unknown S2K type: " + s2k.getType());
                }
            }
            else
            {
                try
                {
                    digest = getDigestInstance("MD5", provider);
                }
                catch (NoSuchAlgorithmException e)
                {
                    throw new PGPException("can't find MD5 digest", e);
                }
                
                for (int i = 0; i != loopCount; i++)
                {
                    digest.update((byte)0);
                }
                
                digest.update(pBytes);
            }
                                
            byte[]    dig = digest.digest();
            
            if (dig.length > (keyBytes.length - generatedBytes))
            {
                System.arraycopy(dig, 0, keyBytes, generatedBytes, keyBytes.length - generatedBytes);
            }
            else
            {
                System.arraycopy(dig, 0, keyBytes, generatedBytes, dig.length);
            }
            
            generatedBytes += dig.length;
            
            loopCount++;
        }
        
        for (int i = 0; i != pBytes.length; i++)
        {
            pBytes[i] = 0;
        }

        return new SecretKeySpec(keyBytes, algName);
    }

    static MessageDigest getDigestInstance(
        String digestName, 
        Provider provider)
        throws NoSuchAlgorithmException
    {
        try
        {       
            return MessageDigest.getInstance(digestName, provider);
        }
        catch (NoSuchAlgorithmException e)
        {
            // try falling back
            return MessageDigest.getInstance(digestName);
        }
    }

    private static String getS2kDigestName(S2K s2k) throws PGPException
    {
        switch (s2k.getHashAlgorithm())
        {
        case HashAlgorithmTags.MD5:
            return "MD5";
        case HashAlgorithmTags.SHA1:
            return "SHA1";
        default:
            throw new PGPException("unknown hash algorithm: " + s2k.getHashAlgorithm());
        }
    }
    
    /**
     * write out the passed in file as a literal data packet.
     * 
     * @param out
     * @param fileType the LiteralData type for the file.
     * @param file
     * 
     * @throws IOException
     */
    public static void writeFileToLiteralData(
        OutputStream    out,
        char            fileType,
        File            file)
        throws IOException
    {
        PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
        OutputStream            pOut = lData.open(out, fileType, file.getName(), file.length(), new Date(file.lastModified()));
        FileInputStream         in = new FileInputStream(file);
        byte[]                  buf = new byte[4096];
        int                     len;
        
        while ((len = in.read(buf)) > 0)
        {
            pOut.write(buf, 0, len);
        }
        
        lData.close();
        in.close();
    }
    
    /**
     * write out the passed in file as a literal data packet in partial packet format.
     * 
     * @param out
     * @param fileType the LiteralData type for the file.
     * @param file
     * @param buffer buffer to be used to chunk the file into partial packets.
     * 
     * @throws IOException
     */
    public static void writeFileToLiteralData(
        OutputStream    out,
        char            fileType,
        File            file,
        byte[]          buffer)
        throws IOException
    {
        PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
        OutputStream            pOut = lData.open(out, fileType, file.getName(), new Date(file.lastModified()), buffer);
        FileInputStream         in = new FileInputStream(file);
        byte[]                  buf = new byte[buffer.length];
        int                     len;
        
        while ((len = in.read(buf)) > 0)
        {
            pOut.write(buf, 0, len);
        }
        
        lData.close();
        in.close();
    }
    
    private static final int READ_AHEAD = 60;
    
    private static boolean isPossiblyBase64(
        int    ch)
    {
        return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') 
                || (ch >= '0' && ch <= '9') || (ch == '+') || (ch == '/')
                || (ch == '\r') || (ch == '\n');
    }
    
    /**
     * Return either an ArmoredInputStream or a BCPGInputStream based on
     * whether the initial characters of the stream are binary PGP encodings or not.
     * 
     * @param in the stream to be wrapped
     * @return a BCPGInputStream
     * @throws IOException
     */
    public static InputStream getDecoderStream(
        InputStream    in) 
        throws IOException
    {
        if (!in.markSupported())
        {
            in = new BufferedInputStream(in);
        }
        
        in.mark(READ_AHEAD);
        
        int    ch = in.read();
        

        if ((ch & 0x80) != 0)
        {
            in.reset();
        
            return in;
        }
        else
        {
            if (!isPossiblyBase64(ch))
            {
                in.reset();
        
                return new ArmoredInputStream(in);
            }
            
            byte[]  buf = new byte[READ_AHEAD];
            int     count = 1;
            int     index = 1;
            
            buf[0] = (byte)ch;
            while (count != READ_AHEAD && (ch = in.read()) >= 0)
            {
                if (!isPossiblyBase64(ch))
                {
                    in.reset();
                    
                    return new ArmoredInputStream(in);
                }
                
                if (ch != '\n' && ch != '\r')
                {
                    buf[index++] = (byte)ch;
                }
                
                count++;
            }
            
            in.reset();
        
            //
            // nothing but new lines, little else, assume regular armoring
            //
            if (count < 4)
            {
                return new ArmoredInputStream(in);
            }
            
            //
            // test our non-blank data
            //
            byte[]    firstBlock = new byte[8];
            
            System.arraycopy(buf, 0, firstBlock, 0, firstBlock.length);

            byte[]    decoded = Base64.decode(firstBlock);
            
            //
            // it's a base64 PGP block.
            //
            if ((decoded[0] & 0x80) != 0)
            {
                return new ArmoredInputStream(in, false);
            }
            
            return new ArmoredInputStream(in);
        }
    }

    static Provider getProvider(String providerName)
        throws NoSuchProviderException
    {
        Provider prov = Security.getProvider(providerName);

        if (prov == null)
        {
            throw new NoSuchProviderException("provider " + providerName + " not found.");
        }

        return prov;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy