PKCS1Encoding.java revision e1142c149e244797ce73b0e7fad40816e447a817
1package org.bouncycastle.crypto.encodings;
2
3import java.security.AccessController;
4import java.security.PrivilegedAction;
5import java.security.SecureRandom;
6
7import org.bouncycastle.crypto.AsymmetricBlockCipher;
8import org.bouncycastle.crypto.CipherParameters;
9import org.bouncycastle.crypto.InvalidCipherTextException;
10import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
11import 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 */
17public 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 (forPrivateKey)
206        {
207            if (type != 2)
208            {
209                throw new InvalidCipherTextException("unknown block type");
210            }
211        }
212        else
213        {
214            if (type != 1)
215            {
216                throw new InvalidCipherTextException("unknown block type");
217            }
218        }
219        // BEGIN android-added
220        if ((type == 1 && forPrivateKey) || (type == 2 && !forPrivateKey))
221        {
222            throw new InvalidCipherTextException("invalid block type " + type);
223        }
224        // END android-added
225
226        if (useStrictLength && block.length != engine.getOutputBlockSize())
227        {
228            throw new InvalidCipherTextException("block incorrect size");
229        }
230
231        //
232        // find and extract the message block.
233        //
234        int start;
235
236        for (start = 1; start != block.length; start++)
237        {
238            byte pad = block[start];
239
240            if (pad == 0)
241            {
242                break;
243            }
244            if (type == 1 && pad != (byte)0xff)
245            {
246                throw new InvalidCipherTextException("block padding incorrect");
247            }
248        }
249
250        start++;           // data should start at the next byte
251
252        if (start > block.length || start < HEADER_LENGTH)
253        {
254            throw new InvalidCipherTextException("no data in block");
255        }
256
257        byte[]  result = new byte[block.length - start];
258
259        System.arraycopy(block, start, result, 0, result.length);
260
261        return result;
262    }
263}
264