Home » crypto-143 » org.bouncycastle.openpgp » [javadoc | source]

    1   package org.bouncycastle.openpgp;
    2   
    3   import java.io.BufferedInputStream;
    4   import java.io.File;
    5   import java.io.FileInputStream;
    6   import java.io.IOException;
    7   import java.io.InputStream;
    8   import java.io.OutputStream;
    9   import java.security.MessageDigest;
   10   import java.security.NoSuchAlgorithmException;
   11   import java.security.NoSuchProviderException;
   12   import java.security.SecureRandom;
   13   import java.security.Security;
   14   import java.security.Provider;
   15   import java.util.Date;
   16   
   17   import javax.crypto.SecretKey;
   18   import javax.crypto.spec.SecretKeySpec;
   19   
   20   import org.bouncycastle.asn1.ASN1InputStream;
   21   import org.bouncycastle.asn1.ASN1Sequence;
   22   import org.bouncycastle.asn1.DERInteger;
   23   import org.bouncycastle.bcpg.ArmoredInputStream;
   24   import org.bouncycastle.bcpg.HashAlgorithmTags;
   25   import org.bouncycastle.bcpg.MPInteger;
   26   import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
   27   import org.bouncycastle.bcpg.S2K;
   28   import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
   29   import org.bouncycastle.util.encoders.Base64;
   30   
   31   /**
   32    * Basic utility class
   33    */
   34   public class PGPUtil
   35       implements HashAlgorithmTags
   36   {
   37       private    static String    defProvider = "BC";
   38   
   39       /**
   40        * Return the provider that will be used by factory classes in situations
   41        * where a provider must be determined on the fly.
   42        * 
   43        * @return String
   44        */
   45       public static String getDefaultProvider()
   46       {
   47           return defProvider;
   48       }
   49       
   50       /**
   51        * Set the provider to be used by the package when it is necessary to 
   52        * find one on the fly.
   53        * 
   54        * @param provider
   55        */
   56       public static void setDefaultProvider(
   57           String    provider)
   58       {
   59           defProvider = provider;
   60       }
   61       
   62       static MPInteger[] dsaSigToMpi(
   63           byte[] encoding) 
   64           throws PGPException
   65       {
   66           ASN1InputStream aIn = new ASN1InputStream(encoding);
   67   
   68           DERInteger i1;
   69           DERInteger i2;
   70   
   71           try
   72           {
   73               ASN1Sequence s = (ASN1Sequence)aIn.readObject();
   74   
   75               i1 = (DERInteger)s.getObjectAt(0);
   76               i2 = (DERInteger)s.getObjectAt(1);
   77           }
   78           catch (IOException e)
   79           {
   80               throw new PGPException("exception encoding signature", e);
   81           }
   82   
   83           MPInteger[] values = new MPInteger[2];
   84           
   85           values[0] = new MPInteger(i1.getValue());
   86           values[1] = new MPInteger(i2.getValue());
   87           
   88           return values;
   89       }
   90       
   91       static String getDigestName(
   92           int        hashAlgorithm)
   93           throws PGPException
   94       {
   95           switch (hashAlgorithm)
   96           {
   97           case HashAlgorithmTags.SHA1:
   98               return "SHA1";
   99           case HashAlgorithmTags.MD2:
  100               return "MD2";
  101           case HashAlgorithmTags.MD5:
  102               return "MD5";
  103           case HashAlgorithmTags.RIPEMD160:
  104               return "RIPEMD160";
  105           case HashAlgorithmTags.SHA256:
  106               return "SHA256";
  107           case HashAlgorithmTags.SHA384:
  108               return "SHA384";
  109           case HashAlgorithmTags.SHA512:
  110               return "SHA512";
  111           case HashAlgorithmTags.SHA224:
  112               return "SHA224";
  113           default:
  114               throw new PGPException("unknown hash algorithm tag in getDigestName: " + hashAlgorithm);
  115           }
  116       }
  117       
  118       static String getSignatureName(
  119           int        keyAlgorithm,
  120           int        hashAlgorithm)
  121           throws PGPException
  122       {
  123           String     encAlg;
  124                   
  125           switch (keyAlgorithm)
  126           {
  127           case PublicKeyAlgorithmTags.RSA_GENERAL:
  128           case PublicKeyAlgorithmTags.RSA_SIGN:
  129               encAlg = "RSA";
  130               break;
  131           case PublicKeyAlgorithmTags.DSA:
  132               encAlg = "DSA";
  133               break;
  134           case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: // in some malformed cases.
  135           case PublicKeyAlgorithmTags.ELGAMAL_GENERAL:
  136               encAlg = "ElGamal";
  137               break;
  138           default:
  139               throw new PGPException("unknown algorithm tag in signature:" + keyAlgorithm);
  140           }
  141   
  142           return getDigestName(hashAlgorithm) + "with" + encAlg;
  143       }
  144       
  145       static String getSymmetricCipherName(
  146           int    algorithm) 
  147           throws PGPException
  148       {
  149           switch (algorithm)
  150           {
  151           case SymmetricKeyAlgorithmTags.NULL:
  152               return null;
  153           case SymmetricKeyAlgorithmTags.TRIPLE_DES:
  154               return "DESEDE";
  155           case SymmetricKeyAlgorithmTags.IDEA:
  156               return "IDEA";
  157           case SymmetricKeyAlgorithmTags.CAST5:
  158               return "CAST5";
  159           case SymmetricKeyAlgorithmTags.BLOWFISH:
  160               return "Blowfish";
  161           case SymmetricKeyAlgorithmTags.SAFER:
  162               return "SAFER";
  163           case SymmetricKeyAlgorithmTags.DES:
  164               return "DES";
  165           case SymmetricKeyAlgorithmTags.AES_128:
  166               return "AES";
  167           case SymmetricKeyAlgorithmTags.AES_192:
  168               return "AES";
  169           case SymmetricKeyAlgorithmTags.AES_256:
  170               return "AES";
  171           case SymmetricKeyAlgorithmTags.TWOFISH:
  172               return "Twofish";
  173           default:
  174               throw new PGPException("unknown symmetric algorithm: " + algorithm);
  175           }
  176       }
  177       
  178       public static SecretKey makeRandomKey(
  179           int             algorithm,
  180           SecureRandom    random) 
  181           throws PGPException
  182       {
  183           String    algName = null;
  184           int        keySize = 0;
  185           
  186           switch (algorithm)
  187           {
  188           case SymmetricKeyAlgorithmTags.TRIPLE_DES:
  189               keySize = 192;
  190               algName = "DES_EDE";
  191               break;
  192           case SymmetricKeyAlgorithmTags.IDEA:
  193               keySize = 128;
  194               algName = "IDEA";
  195               break;
  196           case SymmetricKeyAlgorithmTags.CAST5:
  197               keySize = 128;
  198               algName = "CAST5";
  199               break;
  200           case SymmetricKeyAlgorithmTags.BLOWFISH:
  201               keySize = 128;
  202               algName = "Blowfish";
  203               break;
  204           case SymmetricKeyAlgorithmTags.SAFER:
  205               keySize = 128;
  206               algName = "SAFER";
  207               break;
  208           case SymmetricKeyAlgorithmTags.DES:
  209               keySize = 64;
  210               algName = "DES";
  211               break;
  212           case SymmetricKeyAlgorithmTags.AES_128:
  213               keySize = 128;
  214               algName = "AES";
  215               break;
  216           case SymmetricKeyAlgorithmTags.AES_192:
  217               keySize = 192;
  218               algName = "AES";
  219               break;
  220           case SymmetricKeyAlgorithmTags.AES_256:
  221               keySize = 256;
  222               algName = "AES";
  223               break;
  224           case SymmetricKeyAlgorithmTags.TWOFISH:
  225               keySize = 256;
  226               algName = "Twofish";
  227               break;
  228           default:
  229               throw new PGPException("unknown symmetric algorithm: " + algorithm);
  230           }
  231           
  232           byte[]    keyBytes = new byte[(keySize + 7) / 8];
  233           
  234           random.nextBytes(keyBytes);
  235           
  236           return new SecretKeySpec(keyBytes, algName);
  237       }
  238       
  239       public static SecretKey makeKeyFromPassPhrase(
  240           int       algorithm,
  241           char[]    passPhrase,
  242           String    provider) 
  243           throws NoSuchProviderException, PGPException
  244       {
  245           return makeKeyFromPassPhrase(algorithm, null, passPhrase, provider);
  246       }
  247       
  248       public static SecretKey makeKeyFromPassPhrase(
  249           int     algorithm,
  250           S2K     s2k,
  251           char[]  passPhrase,
  252           String  provider) 
  253           throws PGPException, NoSuchProviderException
  254       {
  255           Provider prov = getProvider(provider);
  256   
  257           return makeKeyFromPassPhrase(algorithm, s2k, passPhrase, prov);
  258       }
  259   
  260       public static SecretKey makeKeyFromPassPhrase(
  261           int     algorithm,
  262           S2K     s2k,
  263           char[]  passPhrase,
  264           Provider provider)
  265           throws PGPException, NoSuchProviderException
  266       {
  267           String    algName = null;
  268           int        keySize = 0;
  269           
  270           switch (algorithm)
  271           {
  272           case SymmetricKeyAlgorithmTags.TRIPLE_DES:
  273               keySize = 192;
  274               algName = "DES_EDE";
  275               break;
  276           case SymmetricKeyAlgorithmTags.IDEA:
  277               keySize = 128;
  278               algName = "IDEA";
  279               break;
  280           case SymmetricKeyAlgorithmTags.CAST5:
  281               keySize = 128;
  282               algName = "CAST5";
  283               break;
  284           case SymmetricKeyAlgorithmTags.BLOWFISH:
  285               keySize = 128;
  286               algName = "Blowfish";
  287               break;
  288           case SymmetricKeyAlgorithmTags.SAFER:
  289               keySize = 128;
  290               algName = "SAFER";
  291               break;
  292           case SymmetricKeyAlgorithmTags.DES:
  293               keySize = 64;
  294               algName = "DES";
  295               break;
  296           case SymmetricKeyAlgorithmTags.AES_128:
  297               keySize = 128;
  298               algName = "AES";
  299               break;
  300           case SymmetricKeyAlgorithmTags.AES_192:
  301               keySize = 192;
  302               algName = "AES";
  303               break;
  304           case SymmetricKeyAlgorithmTags.AES_256:
  305               keySize = 256;
  306               algName = "AES";
  307               break;
  308           case SymmetricKeyAlgorithmTags.TWOFISH:
  309               keySize = 256;
  310               algName = "Twofish";
  311               break;
  312           default:
  313               throw new PGPException("unknown symmetric algorithm: " + algorithm);
  314           }
  315           
  316           byte[]           pBytes = new byte[passPhrase.length];
  317           MessageDigest    digest;
  318                       
  319           for (int i = 0; i != passPhrase.length; i++)
  320           {
  321               pBytes[i] = (byte)passPhrase[i];
  322           }
  323           
  324           byte[]    keyBytes = new byte[(keySize + 7) / 8];
  325           
  326           int    generatedBytes = 0;
  327           int    loopCount = 0;
  328           
  329           while (generatedBytes < keyBytes.length)
  330           {
  331               if (s2k != null)
  332               {     
  333                   String digestName = getS2kDigestName(s2k);
  334                   
  335                   try
  336                   {
  337                       digest = getDigestInstance(digestName, provider);
  338                   }
  339                   catch (NoSuchAlgorithmException e)
  340                   {
  341                       throw new PGPException("can't find S2K digest", e);
  342                   }
  343   
  344                   for (int i = 0; i != loopCount; i++)
  345                   {
  346                       digest.update((byte)0);
  347                   }
  348                   
  349                   byte[]    iv = s2k.getIV();
  350                               
  351                   switch (s2k.getType())
  352                   {
  353                   case S2K.SIMPLE:
  354                       digest.update(pBytes);
  355                       break;
  356                   case S2K.SALTED:
  357                       digest.update(iv);
  358                       digest.update(pBytes);
  359                       break;
  360                   case S2K.SALTED_AND_ITERATED:
  361                       long    count = s2k.getIterationCount();
  362                       digest.update(iv);
  363                       digest.update(pBytes);
  364           
  365                       count -= iv.length + pBytes.length;
  366                                   
  367                       while (count > 0)
  368                       {
  369                           if (count < iv.length)
  370                           {
  371                               digest.update(iv, 0, (int)count);
  372                               break;
  373                           }
  374                           else
  375                           {
  376                               digest.update(iv);
  377                               count -= iv.length;
  378                           }
  379           
  380                           if (count < pBytes.length)
  381                           {
  382                               digest.update(pBytes, 0, (int)count);
  383                               count = 0;
  384                           }
  385                           else
  386                           {
  387                               digest.update(pBytes);
  388                               count -= pBytes.length;
  389                           }
  390                       }
  391                       break;
  392                   default:
  393                       throw new PGPException("unknown S2K type: " + s2k.getType());
  394                   }
  395               }
  396               else
  397               {
  398                   try
  399                   {
  400                       digest = getDigestInstance("MD5", provider);
  401                   }
  402                   catch (NoSuchAlgorithmException e)
  403                   {
  404                       throw new PGPException("can't find MD5 digest", e);
  405                   }
  406                   
  407                   for (int i = 0; i != loopCount; i++)
  408                   {
  409                       digest.update((byte)0);
  410                   }
  411                   
  412                   digest.update(pBytes);
  413               }
  414                                   
  415               byte[]    dig = digest.digest();
  416               
  417               if (dig.length > (keyBytes.length - generatedBytes))
  418               {
  419                   System.arraycopy(dig, 0, keyBytes, generatedBytes, keyBytes.length - generatedBytes);
  420               }
  421               else
  422               {
  423                   System.arraycopy(dig, 0, keyBytes, generatedBytes, dig.length);
  424               }
  425               
  426               generatedBytes += dig.length;
  427               
  428               loopCount++;
  429           }
  430           
  431           for (int i = 0; i != pBytes.length; i++)
  432           {
  433               pBytes[i] = 0;
  434           }
  435   
  436           return new SecretKeySpec(keyBytes, algName);
  437       }
  438   
  439       static MessageDigest getDigestInstance(
  440           String digestName, 
  441           Provider provider)
  442           throws NoSuchAlgorithmException
  443       {
  444           try
  445           {       
  446               return MessageDigest.getInstance(digestName, provider);
  447           }
  448           catch (NoSuchAlgorithmException e)
  449           {
  450               // try falling back
  451               return MessageDigest.getInstance(digestName);
  452           }
  453       }
  454   
  455       private static String getS2kDigestName(S2K s2k) throws PGPException
  456       {
  457           switch (s2k.getHashAlgorithm())
  458           {
  459           case HashAlgorithmTags.MD5:
  460               return "MD5";
  461           case HashAlgorithmTags.SHA1:
  462               return "SHA1";
  463           default:
  464               throw new PGPException("unknown hash algorithm: " + s2k.getHashAlgorithm());
  465           }
  466       }
  467       
  468       /**
  469        * write out the passed in file as a literal data packet.
  470        * 
  471        * @param out
  472        * @param fileType the LiteralData type for the file.
  473        * @param file
  474        * 
  475        * @throws IOException
  476        */
  477       public static void writeFileToLiteralData(
  478           OutputStream    out,
  479           char            fileType,
  480           File            file)
  481           throws IOException
  482       {
  483           PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
  484           OutputStream            pOut = lData.open(out, fileType, file.getName(), file.length(), new Date(file.lastModified()));
  485           FileInputStream         in = new FileInputStream(file);
  486           byte[]                  buf = new byte[4096];
  487           int                     len;
  488           
  489           while ((len = in.read(buf)) > 0)
  490           {
  491               pOut.write(buf, 0, len);
  492           }
  493           
  494           lData.close();
  495           in.close();
  496       }
  497       
  498       /**
  499        * write out the passed in file as a literal data packet in partial packet format.
  500        * 
  501        * @param out
  502        * @param fileType the LiteralData type for the file.
  503        * @param file
  504        * @param buffer buffer to be used to chunk the file into partial packets.
  505        * 
  506        * @throws IOException
  507        */
  508       public static void writeFileToLiteralData(
  509           OutputStream    out,
  510           char            fileType,
  511           File            file,
  512           byte[]          buffer)
  513           throws IOException
  514       {
  515           PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
  516           OutputStream            pOut = lData.open(out, fileType, file.getName(), new Date(file.lastModified()), buffer);
  517           FileInputStream         in = new FileInputStream(file);
  518           byte[]                  buf = new byte[buffer.length];
  519           int                     len;
  520           
  521           while ((len = in.read(buf)) > 0)
  522           {
  523               pOut.write(buf, 0, len);
  524           }
  525           
  526           lData.close();
  527           in.close();
  528       }
  529       
  530       private static final int READ_AHEAD = 60;
  531       
  532       private static boolean isPossiblyBase64(
  533           int    ch)
  534       {
  535           return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') 
  536                   || (ch >= '0' && ch <= '9') || (ch == '+') || (ch == '/')
  537                   || (ch == '\r') || (ch == '\n');
  538       }
  539       
  540       /**
  541        * Return either an ArmoredInputStream or a BCPGInputStream based on
  542        * whether the initial characters of the stream are binary PGP encodings or not.
  543        * 
  544        * @param in the stream to be wrapped
  545        * @return a BCPGInputStream
  546        * @throws IOException
  547        */
  548       public static InputStream getDecoderStream(
  549           InputStream    in) 
  550           throws IOException
  551       {
  552           if (!in.markSupported())
  553           {
  554               in = new BufferedInputStream(in);
  555           }
  556           
  557           in.mark(READ_AHEAD);
  558           
  559           int    ch = in.read();
  560           
  561   
  562           if ((ch & 0x80) != 0)
  563           {
  564               in.reset();
  565           
  566               return in;
  567           }
  568           else
  569           {
  570               if (!isPossiblyBase64(ch))
  571               {
  572                   in.reset();
  573           
  574                   return new ArmoredInputStream(in);
  575               }
  576               
  577               byte[]  buf = new byte[READ_AHEAD];
  578               int     count = 1;
  579               int     index = 1;
  580               
  581               buf[0] = (byte)ch;
  582               while (count != READ_AHEAD && (ch = in.read()) >= 0)
  583               {
  584                   if (!isPossiblyBase64(ch))
  585                   {
  586                       in.reset();
  587                       
  588                       return new ArmoredInputStream(in);
  589                   }
  590                   
  591                   if (ch != '\n' && ch != '\r')
  592                   {
  593                       buf[index++] = (byte)ch;
  594                   }
  595                   
  596                   count++;
  597               }
  598               
  599               in.reset();
  600           
  601               //
  602               // nothing but new lines, little else, assume regular armoring
  603               //
  604               if (count < 4)
  605               {
  606                   return new ArmoredInputStream(in);
  607               }
  608               
  609               //
  610               // test our non-blank data
  611               //
  612               byte[]    firstBlock = new byte[8];
  613               
  614               System.arraycopy(buf, 0, firstBlock, 0, firstBlock.length);
  615   
  616               byte[]    decoded = Base64.decode(firstBlock);
  617               
  618               //
  619               // it's a base64 PGP block.
  620               //
  621               if ((decoded[0] & 0x80) != 0)
  622               {
  623                   return new ArmoredInputStream(in, false);
  624               }
  625               
  626               return new ArmoredInputStream(in);
  627           }
  628       }
  629   
  630       static Provider getProvider(String providerName)
  631           throws NoSuchProviderException
  632       {
  633           Provider prov = Security.getProvider(providerName);
  634   
  635           if (prov == null)
  636           {
  637               throw new NoSuchProviderException("provider " + providerName + " not found.");
  638           }
  639   
  640           return prov;
  641       }
  642   }

Home » crypto-143 » org.bouncycastle.openpgp » [javadoc | source]