Home | History | Annotate | Download | only in encodings
      1 package org.bouncycastle.crypto.encodings;
      2 
      3 import java.security.AccessController;
      4 import java.security.PrivilegedAction;
      5 import java.security.SecureRandom;
      6 
      7 import org.bouncycastle.crypto.AsymmetricBlockCipher;
      8 import org.bouncycastle.crypto.CipherParameters;
      9 import org.bouncycastle.crypto.InvalidCipherTextException;
     10 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
     11 import org.bouncycastle.crypto.params.ParametersWithRandom;
     12 
     13 /**
     14  * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this
     15  * depends on your application - see PKCS1 Version 2 for details.
     16  */
     17 public class PKCS1Encoding
     18     implements AsymmetricBlockCipher
     19 {
     20     /**
     21      * some providers fail to include the leading zero in PKCS1 encoded blocks. If you need to
     22      * work with one of these set the system property org.bouncycastle.pkcs1.strict to false.
     23      * <p>
     24      * The system property is checked during construction of the encoding object, it is set to
     25      * true by default.
     26      * </p>
     27      */
     28     public static final String STRICT_LENGTH_ENABLED_PROPERTY = "org.bouncycastle.pkcs1.strict";
     29 
     30     private static final int HEADER_LENGTH = 10;
     31 
     32     private SecureRandom            random;
     33     private AsymmetricBlockCipher   engine;
     34     private boolean                 forEncryption;
     35     private boolean                 forPrivateKey;
     36     private boolean                 useStrictLength;
     37 
     38     /**
     39      * Basic constructor.
     40      * @param cipher
     41      */
     42     public PKCS1Encoding(
     43         AsymmetricBlockCipher   cipher)
     44     {
     45         this.engine = cipher;
     46         this.useStrictLength = useStrict();
     47     }
     48 
     49     //
     50     // for J2ME compatibility
     51     //
     52     private boolean useStrict()
     53     {
     54         // required if security manager has been installed.
     55         String strict = (String)AccessController.doPrivileged(new PrivilegedAction()
     56         {
     57             public Object run()
     58             {
     59                 return System.getProperty(STRICT_LENGTH_ENABLED_PROPERTY);
     60             }
     61         });
     62 
     63         return strict == null || strict.equals("true");
     64     }
     65 
     66     public AsymmetricBlockCipher getUnderlyingCipher()
     67     {
     68         return engine;
     69     }
     70 
     71     public void init(
     72         boolean             forEncryption,
     73         CipherParameters    param)
     74     {
     75         AsymmetricKeyParameter  kParam;
     76 
     77         if (param instanceof ParametersWithRandom)
     78         {
     79             ParametersWithRandom    rParam = (ParametersWithRandom)param;
     80 
     81             this.random = rParam.getRandom();
     82             kParam = (AsymmetricKeyParameter)rParam.getParameters();
     83         }
     84         else
     85         {
     86             this.random = new SecureRandom();
     87             kParam = (AsymmetricKeyParameter)param;
     88         }
     89 
     90         engine.init(forEncryption, param);
     91 
     92         this.forPrivateKey = kParam.isPrivate();
     93         this.forEncryption = forEncryption;
     94     }
     95 
     96     public int getInputBlockSize()
     97     {
     98         int     baseBlockSize = engine.getInputBlockSize();
     99 
    100         if (forEncryption)
    101         {
    102             return baseBlockSize - HEADER_LENGTH;
    103         }
    104         else
    105         {
    106             return baseBlockSize;
    107         }
    108     }
    109 
    110     public int getOutputBlockSize()
    111     {
    112         int     baseBlockSize = engine.getOutputBlockSize();
    113 
    114         if (forEncryption)
    115         {
    116             return baseBlockSize;
    117         }
    118         else
    119         {
    120             return baseBlockSize - HEADER_LENGTH;
    121         }
    122     }
    123 
    124     public byte[] processBlock(
    125         byte[]  in,
    126         int     inOff,
    127         int     inLen)
    128         throws InvalidCipherTextException
    129     {
    130         if (forEncryption)
    131         {
    132             return encodeBlock(in, inOff, inLen);
    133         }
    134         else
    135         {
    136             return decodeBlock(in, inOff, inLen);
    137         }
    138     }
    139 
    140     private byte[] encodeBlock(
    141         byte[]  in,
    142         int     inOff,
    143         int     inLen)
    144         throws InvalidCipherTextException
    145     {
    146         if (inLen > getInputBlockSize())
    147         {
    148             throw new IllegalArgumentException("input data too large");
    149         }
    150 
    151         byte[]  block = new byte[engine.getInputBlockSize()];
    152 
    153         if (forPrivateKey)
    154         {
    155             block[0] = 0x01;                        // type code 1
    156 
    157             for (int i = 1; i != block.length - inLen - 1; i++)
    158             {
    159                 block[i] = (byte)0xFF;
    160             }
    161         }
    162         else
    163         {
    164             random.nextBytes(block);                // random fill
    165 
    166             block[0] = 0x02;                        // type code 2
    167 
    168             //
    169             // a zero byte marks the end of the padding, so all
    170             // the pad bytes must be non-zero.
    171             //
    172             for (int i = 1; i != block.length - inLen - 1; i++)
    173             {
    174                 while (block[i] == 0)
    175                 {
    176                     block[i] = (byte)random.nextInt();
    177                 }
    178             }
    179         }
    180 
    181         block[block.length - inLen - 1] = 0x00;       // mark the end of the padding
    182         System.arraycopy(in, inOff, block, block.length - inLen, inLen);
    183 
    184         return engine.processBlock(block, 0, block.length);
    185     }
    186 
    187     /**
    188      * @exception InvalidCipherTextException if the decrypted block is not in PKCS1 format.
    189      */
    190     private byte[] decodeBlock(
    191         byte[]  in,
    192         int     inOff,
    193         int     inLen)
    194         throws InvalidCipherTextException
    195     {
    196         byte[]  block = engine.processBlock(in, inOff, inLen);
    197 
    198         if (block.length < getOutputBlockSize())
    199         {
    200             throw new InvalidCipherTextException("block truncated");
    201         }
    202 
    203         byte type = block[0];
    204 
    205         if (type != 1 && type != 2)
    206         {
    207             throw new InvalidCipherTextException("unknown block type");
    208         }
    209 
    210         if (useStrictLength && block.length != engine.getOutputBlockSize())
    211         {
    212             throw new InvalidCipherTextException("block incorrect size");
    213         }
    214 
    215         //
    216         // find and extract the message block.
    217         //
    218         int start;
    219 
    220         for (start = 1; start != block.length; start++)
    221         {
    222             byte pad = block[start];
    223 
    224             if (pad == 0)
    225             {
    226                 break;
    227             }
    228             if (type == 1 && pad != (byte)0xff)
    229             {
    230                 throw new InvalidCipherTextException("block padding incorrect");
    231             }
    232         }
    233 
    234         start++;           // data should start at the next byte
    235 
    236         if (start > block.length || start < HEADER_LENGTH)
    237         {
    238             throw new InvalidCipherTextException("no data in block");
    239         }
    240 
    241         byte[]  result = new byte[block.length - start];
    242 
    243         System.arraycopy(block, start, result, 0, result.length);
    244 
    245         return result;
    246     }
    247 }
    248