CipherSpi.java revision e1142c149e244797ce73b0e7fad40816e447a817
1package org.bouncycastle.jcajce.provider.asymmetric.rsa;
2
3import java.io.ByteArrayOutputStream;
4import java.security.AlgorithmParameters;
5import java.security.InvalidAlgorithmParameterException;
6import java.security.InvalidKeyException;
7import java.security.InvalidParameterException;
8import java.security.Key;
9import java.security.NoSuchAlgorithmException;
10import java.security.SecureRandom;
11import java.security.interfaces.RSAPrivateKey;
12import java.security.interfaces.RSAPublicKey;
13import java.security.spec.AlgorithmParameterSpec;
14import java.security.spec.InvalidParameterSpecException;
15import java.security.spec.MGF1ParameterSpec;
16
17import javax.crypto.BadPaddingException;
18import javax.crypto.Cipher;
19import javax.crypto.IllegalBlockSizeException;
20import javax.crypto.NoSuchPaddingException;
21import javax.crypto.spec.OAEPParameterSpec;
22import javax.crypto.spec.PSource;
23
24import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
25import org.bouncycastle.crypto.AsymmetricBlockCipher;
26import org.bouncycastle.crypto.CipherParameters;
27import org.bouncycastle.crypto.Digest;
28import org.bouncycastle.crypto.InvalidCipherTextException;
29// BEGIN android-removed
30// import org.bouncycastle.crypto.encodings.ISO9796d1Encoding;
31// END android-removed
32import org.bouncycastle.crypto.encodings.OAEPEncoding;
33import org.bouncycastle.crypto.encodings.PKCS1Encoding;
34import org.bouncycastle.crypto.engines.RSABlindedEngine;
35import org.bouncycastle.crypto.params.ParametersWithRandom;
36import org.bouncycastle.jcajce.provider.asymmetric.util.BaseCipherSpi;
37import org.bouncycastle.jcajce.provider.util.DigestFactory;
38import org.bouncycastle.jce.provider.BouncyCastleProvider;
39import org.bouncycastle.util.Strings;
40
41public class CipherSpi
42    extends BaseCipherSpi
43{
44    private AsymmetricBlockCipher cipher;
45    private AlgorithmParameterSpec paramSpec;
46    private AlgorithmParameters engineParams;
47    private boolean                 publicKeyOnly = false;
48    private boolean                 privateKeyOnly = false;
49    private ByteArrayOutputStream bOut = new ByteArrayOutputStream();
50
51    public CipherSpi(
52        AsymmetricBlockCipher engine)
53    {
54        cipher = engine;
55    }
56
57    public CipherSpi(
58        OAEPParameterSpec pSpec)
59    {
60        try
61        {
62            initFromSpec(pSpec);
63        }
64        catch (NoSuchPaddingException e)
65        {
66            throw new IllegalArgumentException(e.getMessage());
67        }
68    }
69
70    public CipherSpi(
71        boolean publicKeyOnly,
72        boolean privateKeyOnly,
73        AsymmetricBlockCipher engine)
74    {
75        this.publicKeyOnly = publicKeyOnly;
76        this.privateKeyOnly = privateKeyOnly;
77        cipher = engine;
78    }
79
80    private void initFromSpec(
81        OAEPParameterSpec pSpec)
82        throws NoSuchPaddingException
83    {
84        MGF1ParameterSpec mgfParams = (MGF1ParameterSpec)pSpec.getMGFParameters();
85        Digest digest = DigestFactory.getDigest(mgfParams.getDigestAlgorithm());
86
87        if (digest == null)
88        {
89            throw new NoSuchPaddingException("no match on OAEP constructor for digest algorithm: "+ mgfParams.getDigestAlgorithm());
90        }
91
92        cipher = new OAEPEncoding(new RSABlindedEngine(), digest, ((PSource.PSpecified)pSpec.getPSource()).getValue());
93        paramSpec = pSpec;
94    }
95
96    protected int engineGetBlockSize()
97    {
98        try
99        {
100            return cipher.getInputBlockSize();
101        }
102        catch (NullPointerException e)
103        {
104            throw new IllegalStateException("RSA Cipher not initialised");
105        }
106    }
107
108    protected int engineGetKeySize(
109        Key key)
110    {
111        if (key instanceof RSAPrivateKey)
112        {
113            RSAPrivateKey k = (RSAPrivateKey)key;
114
115            return k.getModulus().bitLength();
116        }
117        else if (key instanceof RSAPublicKey)
118        {
119            RSAPublicKey k = (RSAPublicKey)key;
120
121            return k.getModulus().bitLength();
122        }
123
124        throw new IllegalArgumentException("not an RSA key!");
125    }
126
127    protected int engineGetOutputSize(
128        int     inputLen)
129    {
130        try
131        {
132            return cipher.getOutputBlockSize();
133        }
134        catch (NullPointerException e)
135        {
136            throw new IllegalStateException("RSA Cipher not initialised");
137        }
138    }
139
140    protected AlgorithmParameters engineGetParameters()
141    {
142        if (engineParams == null)
143        {
144            if (paramSpec != null)
145            {
146                try
147                {
148                    engineParams = AlgorithmParameters.getInstance("OAEP", BouncyCastleProvider.PROVIDER_NAME);
149                    engineParams.init(paramSpec);
150                }
151                catch (Exception e)
152                {
153                    throw new RuntimeException(e.toString());
154                }
155            }
156        }
157
158        return engineParams;
159    }
160
161    protected void engineSetMode(
162        String mode)
163        throws NoSuchAlgorithmException
164    {
165        String md = Strings.toUpperCase(mode);
166
167        if (md.equals("NONE") || md.equals("ECB"))
168        {
169            return;
170        }
171
172        if (md.equals("1"))
173        {
174            privateKeyOnly = true;
175            publicKeyOnly = false;
176            return;
177        }
178        else if (md.equals("2"))
179        {
180            privateKeyOnly = false;
181            publicKeyOnly = true;
182            return;
183        }
184
185        throw new NoSuchAlgorithmException("can't support mode " + mode);
186    }
187
188    protected void engineSetPadding(
189        String padding)
190        throws NoSuchPaddingException
191    {
192        String pad = Strings.toUpperCase(padding);
193
194        if (pad.equals("NOPADDING"))
195        {
196            cipher = new RSABlindedEngine();
197        }
198        else if (pad.equals("PKCS1PADDING"))
199        {
200            cipher = new PKCS1Encoding(new RSABlindedEngine());
201        }
202        // BEGIN android-removed
203        // else if (pad.equals("ISO9796-1PADDING"))
204        // {
205        //     cipher = new ISO9796d1Encoding(new RSABlindedEngine());
206        // }
207        // END android-removed
208        else if (pad.equals("OAEPWITHMD5ANDMGF1PADDING"))
209        {
210            initFromSpec(new OAEPParameterSpec("MD5", "MGF1", new MGF1ParameterSpec("MD5"), PSource.PSpecified.DEFAULT));
211        }
212        else if (pad.equals("OAEPPADDING"))
213        {
214            initFromSpec(OAEPParameterSpec.DEFAULT);
215        }
216        else if (pad.equals("OAEPWITHSHA1ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-1ANDMGF1PADDING"))
217        {
218            initFromSpec(OAEPParameterSpec.DEFAULT);
219        }
220        // BEGIN android-removed
221        // else if (pad.equals("OAEPWITHSHA224ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-224ANDMGF1PADDING"))
222        // {
223        //     initFromSpec(new OAEPParameterSpec("SHA-224", "MGF1", new MGF1ParameterSpec("SHA-224"), PSource.PSpecified.DEFAULT));
224        // }
225        // END android-removed
226        else if (pad.equals("OAEPWITHSHA256ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-256ANDMGF1PADDING"))
227        {
228            initFromSpec(new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));
229        }
230        else if (pad.equals("OAEPWITHSHA384ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-384ANDMGF1PADDING"))
231        {
232            initFromSpec(new OAEPParameterSpec("SHA-384", "MGF1", MGF1ParameterSpec.SHA384, PSource.PSpecified.DEFAULT));
233        }
234        else if (pad.equals("OAEPWITHSHA512ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-512ANDMGF1PADDING"))
235        {
236            initFromSpec(new OAEPParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, PSource.PSpecified.DEFAULT));
237        }
238        else
239        {
240            throw new NoSuchPaddingException(padding + " unavailable with RSA.");
241        }
242    }
243
244    protected void engineInit(
245        int                     opmode,
246        Key key,
247        AlgorithmParameterSpec params,
248        SecureRandom random)
249    throws InvalidKeyException, InvalidAlgorithmParameterException
250    {
251        CipherParameters param;
252
253        if (params == null || params instanceof OAEPParameterSpec)
254        {
255            if (key instanceof RSAPublicKey)
256            {
257                if (privateKeyOnly && opmode == Cipher.ENCRYPT_MODE)
258                {
259                    throw new InvalidKeyException(
260                                "mode 1 requires RSAPrivateKey");
261                }
262
263                param = RSAUtil.generatePublicKeyParameter((RSAPublicKey)key);
264            }
265            else if (key instanceof RSAPrivateKey)
266            {
267                if (publicKeyOnly && opmode == Cipher.ENCRYPT_MODE)
268                {
269                    throw new InvalidKeyException(
270                                "mode 2 requires RSAPublicKey");
271                }
272
273                param = RSAUtil.generatePrivateKeyParameter((RSAPrivateKey)key);
274            }
275            else
276            {
277                throw new InvalidKeyException("unknown key type passed to RSA");
278            }
279
280            if (params != null)
281            {
282                OAEPParameterSpec spec = (OAEPParameterSpec)params;
283
284                paramSpec = params;
285
286                if (!spec.getMGFAlgorithm().equalsIgnoreCase("MGF1") && !spec.getMGFAlgorithm().equals(PKCSObjectIdentifiers.id_mgf1.getId()))
287                {
288                    throw new InvalidAlgorithmParameterException("unknown mask generation function specified");
289                }
290
291                if (!(spec.getMGFParameters() instanceof MGF1ParameterSpec))
292                {
293                    throw new InvalidAlgorithmParameterException("unkown MGF parameters");
294                }
295
296                Digest digest = DigestFactory.getDigest(spec.getDigestAlgorithm());
297
298                if (digest == null)
299                {
300                    throw new InvalidAlgorithmParameterException("no match on digest algorithm: "+ spec.getDigestAlgorithm());
301                }
302
303                MGF1ParameterSpec mgfParams = (MGF1ParameterSpec)spec.getMGFParameters();
304                Digest mgfDigest = DigestFactory.getDigest(mgfParams.getDigestAlgorithm());
305
306                if (mgfDigest == null)
307                {
308                    throw new InvalidAlgorithmParameterException("no match on MGF digest algorithm: "+ mgfParams.getDigestAlgorithm());
309                }
310
311                cipher = new OAEPEncoding(new RSABlindedEngine(), digest, mgfDigest, ((PSource.PSpecified)spec.getPSource()).getValue());
312            }
313        }
314        else
315        {
316            throw new IllegalArgumentException("unknown parameter type.");
317        }
318
319        if (!(cipher instanceof RSABlindedEngine))
320        {
321            if (random != null)
322            {
323                param = new ParametersWithRandom(param, random);
324            }
325            else
326            {
327                param = new ParametersWithRandom(param, new SecureRandom());
328            }
329        }
330
331        bOut.reset();
332
333        switch (opmode)
334        {
335        case Cipher.ENCRYPT_MODE:
336        case Cipher.WRAP_MODE:
337            cipher.init(true, param);
338            break;
339        case Cipher.DECRYPT_MODE:
340        case Cipher.UNWRAP_MODE:
341            cipher.init(false, param);
342            break;
343        default:
344            throw new InvalidParameterException("unknown opmode " + opmode + " passed to RSA");
345        }
346    }
347
348    protected void engineInit(
349        int                 opmode,
350        Key key,
351        AlgorithmParameters params,
352        SecureRandom random)
353    throws InvalidKeyException, InvalidAlgorithmParameterException
354    {
355        AlgorithmParameterSpec paramSpec = null;
356
357        if (params != null)
358        {
359            try
360            {
361                paramSpec = params.getParameterSpec(OAEPParameterSpec.class);
362            }
363            catch (InvalidParameterSpecException e)
364            {
365                throw new InvalidAlgorithmParameterException("cannot recognise parameters: " + e.toString(), e);
366            }
367        }
368
369        engineParams = params;
370        engineInit(opmode, key, paramSpec, random);
371    }
372
373    protected void engineInit(
374        int                 opmode,
375        Key key,
376        SecureRandom random)
377    throws InvalidKeyException
378    {
379        try
380        {
381            engineInit(opmode, key, (AlgorithmParameterSpec)null, random);
382        }
383        catch (InvalidAlgorithmParameterException e)
384        {
385            // this shouldn't happen
386            throw new InvalidKeyException("Eeeek! " + e.toString(), e);
387        }
388    }
389
390    protected byte[] engineUpdate(
391        byte[]  input,
392        int     inputOffset,
393        int     inputLen)
394    {
395        bOut.write(input, inputOffset, inputLen);
396
397        if (cipher instanceof RSABlindedEngine)
398        {
399            if (bOut.size() > cipher.getInputBlockSize() + 1)
400            {
401                throw new ArrayIndexOutOfBoundsException("too much data for RSA block");
402            }
403        }
404        else
405        {
406            if (bOut.size() > cipher.getInputBlockSize())
407            {
408                throw new ArrayIndexOutOfBoundsException("too much data for RSA block");
409            }
410        }
411
412        return null;
413    }
414
415    protected int engineUpdate(
416        byte[]  input,
417        int     inputOffset,
418        int     inputLen,
419        byte[]  output,
420        int     outputOffset)
421    {
422        bOut.write(input, inputOffset, inputLen);
423
424        if (cipher instanceof RSABlindedEngine)
425        {
426            if (bOut.size() > cipher.getInputBlockSize() + 1)
427            {
428                throw new ArrayIndexOutOfBoundsException("too much data for RSA block");
429            }
430        }
431        else
432        {
433            if (bOut.size() > cipher.getInputBlockSize())
434            {
435                throw new ArrayIndexOutOfBoundsException("too much data for RSA block");
436            }
437        }
438
439        return 0;
440    }
441
442    protected byte[] engineDoFinal(
443        byte[]  input,
444        int     inputOffset,
445        int     inputLen)
446        throws IllegalBlockSizeException, BadPaddingException
447    {
448        if (input != null)
449        {
450            bOut.write(input, inputOffset, inputLen);
451        }
452
453        if (cipher instanceof RSABlindedEngine)
454        {
455            if (bOut.size() > cipher.getInputBlockSize() + 1)
456            {
457                throw new ArrayIndexOutOfBoundsException("too much data for RSA block");
458            }
459        }
460        else
461        {
462            if (bOut.size() > cipher.getInputBlockSize())
463            {
464                throw new ArrayIndexOutOfBoundsException("too much data for RSA block");
465            }
466        }
467
468        try
469        {
470            byte[]  bytes = bOut.toByteArray();
471
472            bOut.reset();
473
474            return cipher.processBlock(bytes, 0, bytes.length);
475        }
476        catch (InvalidCipherTextException e)
477        {
478            throw new BadPaddingException(e.getMessage());
479        }
480    }
481
482    protected int engineDoFinal(
483        byte[]  input,
484        int     inputOffset,
485        int     inputLen,
486        byte[]  output,
487        int     outputOffset)
488        throws IllegalBlockSizeException, BadPaddingException
489    {
490        if (input != null)
491        {
492            bOut.write(input, inputOffset, inputLen);
493        }
494
495        if (cipher instanceof RSABlindedEngine)
496        {
497            if (bOut.size() > cipher.getInputBlockSize() + 1)
498            {
499                throw new ArrayIndexOutOfBoundsException("too much data for RSA block");
500            }
501        }
502        else
503        {
504            if (bOut.size() > cipher.getInputBlockSize())
505            {
506                throw new ArrayIndexOutOfBoundsException("too much data for RSA block");
507            }
508        }
509
510        byte[]  out;
511
512        try
513        {
514            byte[]  bytes = bOut.toByteArray();
515
516            out = cipher.processBlock(bytes, 0, bytes.length);
517        }
518        catch (InvalidCipherTextException e)
519        {
520            throw new BadPaddingException(e.getMessage());
521        }
522        finally
523        {
524            bOut.reset();
525        }
526
527        for (int i = 0; i != out.length; i++)
528        {
529            output[outputOffset + i] = out[i];
530        }
531
532        return out.length;
533    }
534
535    /**
536     * classes that inherit from us.
537     */
538
539    static public class NoPadding
540        extends CipherSpi
541    {
542        public NoPadding()
543        {
544            super(new RSABlindedEngine());
545        }
546    }
547
548    // BEGIN android-removed
549    // static public class PKCS1v1_5Padding
550    //     extends CipherSpi
551    // {
552    //     public PKCS1v1_5Padding()
553    //     {
554    //         super(new PKCS1Encoding(new RSABlindedEngine()));
555    //     }
556    // }
557    //
558    // static public class PKCS1v1_5Padding_PrivateOnly
559    //     extends CipherSpi
560    // {
561    //     public PKCS1v1_5Padding_PrivateOnly()
562    //     {
563    //         super(false, true, new PKCS1Encoding(new RSABlindedEngine()));
564    //     }
565    // }
566    //
567    // static public class PKCS1v1_5Padding_PublicOnly
568    //     extends CipherSpi
569    // {
570    //     public PKCS1v1_5Padding_PublicOnly()
571    //     {
572    //         super(true, false, new PKCS1Encoding(new RSABlindedEngine()));
573    //     }
574    // }
575    //
576    // static public class OAEPPadding
577    //     extends CipherSpi
578    // {
579    //     public OAEPPadding()
580    //     {
581    //         super(OAEPParameterSpec.DEFAULT);
582    //     }
583    // }
584    //
585    // static public class ISO9796d1Padding
586    //     extends CipherSpi
587    // {
588    //     public ISO9796d1Padding()
589    //     {
590    //         super(new ISO9796d1Encoding(new RSABlindedEngine()));
591    //     }
592    // }
593    // END android-removed
594}
595