1    package org.bouncycastle.crypto.encodings;
2    
3    import java.math.BigInteger;
4    import java.security.SecureRandom;
5    
6    import org.bouncycastle.crypto.CipherParameters;
7    import org.bouncycastle.crypto.AsymmetricBlockCipher;
8    import org.bouncycastle.crypto.InvalidCipherTextException;
9    import org.bouncycastle.crypto.params.ParametersWithRandom;
10   import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
11   
12   /**
13    * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this
14    * depends on your application - see PKCS1 Version 2 for details.
15    */
16   public class PKCS1Encoding
17       implements AsymmetricBlockCipher
18   {
19       private static int      HEADER_LENGTH = 10;
20   
21       private SecureRandom            random;
22       private AsymmetricBlockCipher   engine;
23       private boolean                 forEncryption;
24       private boolean                 forPrivateKey;
25   
26       public PKCS1Encoding(
27           AsymmetricBlockCipher   cipher)
28       {
29           this.engine = cipher;
30       }   
31   
32       public AsymmetricBlockCipher getUnderlyingCipher()
33       {
34           return engine;
35       }
36   
37       public void init(
38           boolean             forEncryption,
39           CipherParameters    param)
40       {
41           AsymmetricKeyParameter  kParam;
42   
43           if (param instanceof ParametersWithRandom)
44           {
45               ParametersWithRandom    rParam = (ParametersWithRandom)param;
46   
47               this.random = rParam.getRandom();
48               kParam = (AsymmetricKeyParameter)rParam.getParameters();
49           }
50           else
51           {
52               this.random = new SecureRandom();
53               kParam = (AsymmetricKeyParameter)param;
54           }
55   
56           engine.init(forEncryption, kParam);
57   
58           this.forPrivateKey = kParam.isPrivate();
59           this.forEncryption = forEncryption;
60       }
61   
62       public int getInputBlockSize()
63       {
64           int     baseBlockSize = engine.getInputBlockSize();
65   
66           if (forEncryption)
67           {
68               return baseBlockSize - HEADER_LENGTH;
69           }
70           else
71           {
72               return baseBlockSize;
73           }
74       }
75   
76       public int getOutputBlockSize()
77       {
78           int     baseBlockSize = engine.getOutputBlockSize();
79   
80           if (forEncryption)
81           {
82               return baseBlockSize;
83           }
84           else
85           {
86               return baseBlockSize - HEADER_LENGTH;
87           }
88       }
89   
90       public byte[] processBlock(
91           byte[]  in,
92           int     inOff,
93           int     inLen)
94           throws InvalidCipherTextException
95       {
96           if (forEncryption)
97           {
98               return encodeBlock(in, inOff, inLen);
99           }
100          else
101          {
102              return decodeBlock(in, inOff, inLen);
103          }
104      }
105  
106      private byte[] encodeBlock(
107          byte[]  in,
108          int     inOff,
109          int     inLen)
110          throws InvalidCipherTextException
111      {
112          byte[]  block = new byte[engine.getInputBlockSize()];
113  
114          if (forPrivateKey)
115          {
116              block[0] = 0x01;                        // type code 1
117  
118              for (int i = 1; i != block.length - inLen - 1; i++)
119              {
120                  block[i] = (byte)0xFF;
121              }
122          }
123          else
124          {
125              random.nextBytes(block);                // random fill
126  
127              block[0] = 0x02;                        // type code 2
128  
129              //
130              // a zero byte marks the end of the padding, so all
131              // the pad bytes must be non-zero.
132              //
133              for (int i = 1; i != block.length - inLen - 1; i++)
134              {
135                  while (block[i] == 0)
136                  {
137                      block[i] = (byte)random.nextInt();
138                  }
139              }
140          }
141  
142          block[block.length - inLen - 1] = 0x00;       // mark the end of the padding
143          System.arraycopy(in, inOff, block, block.length - inLen, inLen);
144  
145          return engine.processBlock(block, 0, block.length);
146      }
147  
148      /**
149       * @exception InvalidCipherTextException if the decrypted block is not in PKCS1 format.
150       */
151      private byte[] decodeBlock(
152          byte[]  in,
153          int     inOff,
154          int     inLen)
155          throws InvalidCipherTextException
156      {
157          byte[]  block = engine.processBlock(in, inOff, inLen);
158  
159          if (block.length < getOutputBlockSize())
160          {
161              throw new InvalidCipherTextException("block truncated");
162          }
163  
164          if (block[0] != 1 && block[0] != 2)
165          {
166              throw new InvalidCipherTextException("unknown block type");
167          }
168  
169          //
170          // find and extract the message block.
171          //
172          int start;
173  
174          for (start = 1; start != block.length; start++)
175          {
176              if (block[start] == 0)
177              {
178                  break;
179              }
180          }
181  
182          start++;           // data should start at the next byte
183  
184          if (start >= block.length || start < HEADER_LENGTH)
185          {
186              throw new InvalidCipherTextException("no data in block");
187          }
188  
189          byte[]  result = new byte[block.length - start];
190  
191          System.arraycopy(block, start, result, 0, result.length);
192  
193          return result;
194      }
195  }
196