1 /* $Id: RSACipher_ECB_PKCS1.java,v 1.12 2003/02/17 18:25:19 gelderen Exp $
2  *
3  * Copyright (C) 1995-2000 The Cryptix Foundation Limited.
4  * All rights reserved.
5  *
6  * Use, modification, copying and distribution of this software is subject
7  * the terms and conditions of the Cryptix General Licence. You should have
8  * received a copy of the Cryptix General Licence along with this library;
9  * if not, you can download a copy from http://www.cryptix.org/ .
10  */
11 package cryptix.jce.provider.rsa;
12 
13 import javax.crypto.Cipher;
14 import javax.crypto.CipherSpi;
15 import javax.crypto.KeyGenerator;
16 import javax.crypto.SecretKeyFactory;
17 import javax.crypto.NoSuchPaddingException;
18 import javax.crypto.BadPaddingException;
19 import javax.crypto.ShortBufferException;
20 import javax.crypto.IllegalBlockSizeException;
21 import javax.crypto.spec.SecretKeySpec;
22 
23 import java.math.BigInteger;
24 import java.security.Key;
25 import java.security.SecureRandom;
26 import java.security.spec.AlgorithmParameterSpec;
27 import java.security.AlgorithmParameters;
28 import java.security.NoSuchProviderException;
29 import java.security.NoSuchAlgorithmException;
30 import java.security.InvalidKeyException;
31 import java.security.InvalidAlgorithmParameterException;
32 import java.security.spec.InvalidKeySpecException;
33 import java.security.interfaces.RSAPublicKey;
34 import java.security.interfaces.RSAPrivateKey;
35 import java.security.interfaces.RSAPrivateCrtKey;
36 
37 
38 /**
39  * <B>Please read the comments in the source.</B>
40  *
41  * @author Paul Waserbrot (pw@cryptix.org)
42  * @version $Revision: 1.12 $
43  */
44 public final class RSACipher_ECB_PKCS1 extends CipherSpi {
45 
46     private BigInteger n, e, p, q, u;
47 
48     private boolean decrypt;
49 
RSACipher_ECB_PKCS1()50     public RSACipher_ECB_PKCS1() {
51         super();
52     }
53 
54     protected final void
engineSetMode(String mode)55     engineSetMode(String mode)
56     throws NoSuchAlgorithmException {
57         if (!mode.equalsIgnoreCase("ECB"))
58             throw new NoSuchAlgorithmException("Wrong mode type!");
59     }
60 
61 
62     protected final void
engineSetPadding(String padding)63     engineSetPadding(String padding)
64     throws NoSuchPaddingException {
65         if (!padding.equalsIgnoreCase("PKCS1")
66             && !padding.equalsIgnoreCase("PKCS#1")
67             && !padding.equalsIgnoreCase("PKCS1Padding"))
68         {
69             // Added as many cases i could think of.. (pw)
70             throw new NoSuchPaddingException("Wrong padding scheme!");
71         }
72     }
73 
74 
75     protected final int
engineGetBlockSize()76     engineGetBlockSize() {
77         return (n.bitLength()+7)/8;
78     }
79 
80 
81     protected final int
engineGetOutputSize(int inputLen)82     engineGetOutputSize(int inputLen) {
83         return (inputLen < this.engineGetBlockSize()+1) ?
84             this.engineGetBlockSize() + 1: inputLen;
85     }
86 
87 
88     protected final byte[]
engineGetIV()89     engineGetIV() {
90         return null;
91     }
92 
93 
94     protected final AlgorithmParameters
engineGetParameters()95     engineGetParameters() {
96         return null;
97     }
98 
99 
100     protected final void
engineInit(int opmode, Key key, SecureRandom random)101     engineInit(int opmode, Key key, SecureRandom random)
102     throws InvalidKeyException {
103 
104         if (!(key instanceof RSAPrivateKey) && !(key instanceof RSAPublicKey))
105             throw new InvalidKeyException(
106               "Key must be instance of either RSAPublicKey or RSAPrivateKey!");
107 
108         decrypt = ((opmode == Cipher.DECRYPT_MODE) ||
109                    (opmode == Cipher.UNWRAP_MODE));
110 
111         if (decrypt) {
112             n = ((RSAPrivateKey)key).getModulus();
113             e = ((RSAPrivateKey)key).getPrivateExponent();
114 
115         } else {
116             n = ((RSAPublicKey)key).getModulus();
117             e = ((RSAPublicKey)key).getPublicExponent();
118         }
119 
120         if (key instanceof RSAPrivateCrtKey) {
121             p = ((RSAPrivateCrtKey)key).getPrimeP();
122             q = ((RSAPrivateCrtKey)key).getPrimeQ();
123             u = ((RSAPrivateCrtKey)key).getCrtCoefficient();
124         } else {
125             p = q = u = null;
126         }
127     }
128 
129 
130     protected final void
engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random)131     engineInit(int opmode, Key key, AlgorithmParameterSpec params,
132                SecureRandom random)
133     throws InvalidKeyException, InvalidAlgorithmParameterException {
134         throw new InvalidAlgorithmParameterException(
135             "This cipher do not support AlgorithmParameterSpecs");
136     }
137 
138 
139     protected final void
engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)140     engineInit(int opmode, Key key, AlgorithmParameters params,
141                SecureRandom random)
142     throws InvalidKeyException, InvalidAlgorithmParameterException {
143         throw new InvalidAlgorithmParameterException(
144             "This cipher do not support AlgorithmParameters");
145     }
146 
147 
148     protected final byte[]
engineUpdate(byte[] input, int inputOffset, int inputLen)149     engineUpdate(byte[] input, int inputOffset, int inputLen) {
150         throw new RuntimeException("You can't do an update when using PKCS1!");
151         /* Or should we buffer everything until doFinal
152          * or maybe .update() the buffer as many blocksizes a possible and
153          * then buffer (like we do for blockciphers)?
154          * IMO a bad idea! (pw)
155          */
156     }
157 
158 
159     protected final int
engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)160     engineUpdate(byte[] input, int inputOffset, int inputLen,
161                  byte[] output, int outputOffset)
162     throws ShortBufferException {
163         throw new RuntimeException("You can't do an update when using PKCS1!");
164         /* Or should we buffer everything until doFinal??
165          * or maybe .update() the buffer as many blocksizes a possible and
166          * then buffer (like we do for blockciphers)?
167          * IMO a bad idea! (pw)
168          */
169     }
170 
171 
172     protected final byte[]
engineDoFinal(byte[] input, int inputOffset, int inputLen)173     engineDoFinal(byte[] input, int inputOffset, int inputLen)
174     throws IllegalBlockSizeException, BadPaddingException {
175         byte [] o = new byte[this.engineGetOutputSize(inputLen)];
176         int ret;
177         try {
178             ret = this.engineDoFinal(input, inputOffset, inputLen, o, 0);
179             if (ret == o.length)
180                 return o;
181         } catch (ShortBufferException e) {
182             throw new RuntimeException("PANIC: Should not happned!");
183         }
184 
185         // If the buffer returned is smaller than what we allocated first.
186         byte [] r = new byte[ret];
187         System.arraycopy(o, 0, r, 0, ret);
188         return r;
189     }
190 
191 
192     protected final int
engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)193     engineDoFinal(byte[] input, int inputOffset, int inputLen,
194                   byte[] output, int outputOffset)
195     throws ShortBufferException, IllegalBlockSizeException, BadPaddingException
196     {
197         if (output.length < this.engineGetOutputSize(inputLen))
198             throw new ShortBufferException("Output buffer too small!");
199 
200         byte[] blub = new byte[inputLen];
201         System.arraycopy(input, inputOffset, blub, 0, inputLen);
202 
203         byte [] b;
204         BigInteger bi, res;
205         if (decrypt) {
206 
207             bi = new BigInteger(1, blub);
208             if(bi.compareTo(n)!=-1)
209                 throw new RuntimeException("TT");
210             res = RSAAlgorithm.rsa(bi, n, e, p, q, u);
211             b = res.toByteArray();
212             return unpad(b, b.length, 0,
213                          output, outputOffset);
214         } else {
215 
216             /* FIXME: Do so we choose right block type out of the keytype?
217              * (pw)
218              */
219             bi = new BigInteger(1, pad(blub, blub.length, 0, 0x02));
220             if(bi.compareTo(n)!=-1)
221                 throw new RuntimeException("TT");
222 
223             res = RSAAlgorithm.rsa(bi, this.n, this.e);
224             if(res.compareTo(n)!=-1)
225                 throw new RuntimeException("TT");
226 
227             int blockSize = engineGetBlockSize();
228 
229             b = res.toByteArray();
230             if( b.length-1 > blockSize )
231                 throw new RuntimeException("YY");
232 
233             if( b.length > blockSize ) {
234                 byte[] t = new byte[blockSize];
235                 System.arraycopy(b, 1, t, 0, blockSize);
236                 b = t;
237             }
238 
239             for(int i=0; i<blockSize; i++)
240                 output[outputOffset+i] = 0x00;
241 
242             int bOff = blockSize - b.length;
243 
244             System.arraycopy(b, 0, output, outputOffset + bOff, b.length);
245             return b.length + bOff;
246         }
247     }
248 
249 
250     protected byte[]
engineWrap(Key key)251     engineWrap(Key key)
252     throws IllegalBlockSizeException, InvalidKeyException {
253         // FIXME: Should we do some sanity check of the key?? (pw)
254         String format = key.getFormat();
255         // FIXME: Add so we take more than just keys from blockciphers (pw)
256         if (format == null || !format.equalsIgnoreCase("RAW"))
257             throw new InvalidKeyException("Wrong format on key!");
258         byte [] buf = key.getEncoded();
259         try {
260             return this.engineDoFinal(buf, 0, buf.length);
261         } catch (BadPaddingException e) {
262             throw new RuntimeException("PANIC: This should not happend!");
263         }
264     }
265 
266     protected Key
engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)267     engineUnwrap(byte[] wrappedKey,
268                  String wrappedKeyAlgorithm,
269                  int wrappedKeyType)
270     throws InvalidKeyException, NoSuchAlgorithmException {
271         // FIXME: Add so we also support private and publickeys (pw)
272         if (wrappedKeyType != Cipher.SECRET_KEY)
273             throw new InvalidKeyException("Wrong keytype!");
274 
275 
276         try {
277             // FIXME: HACK! Do test to see if we support the algorithm
278             // Do we need to do this??? (pw)
279             KeyGenerator.getInstance(wrappedKeyAlgorithm, "Cryptix");
280 
281             byte [] buf = this.engineDoFinal(wrappedKey, 0, wrappedKey.length);
282 
283             // FIXME: Shall we check for DES keys and use DESKeySpec? (pw)
284             SecretKeySpec sks = new SecretKeySpec(buf, 0, buf.length,
285                                                   wrappedKeyAlgorithm);
286 
287             SecretKeyFactory skf =
288                 SecretKeyFactory.getInstance(wrappedKeyAlgorithm);
289             return skf.generateSecret(sks);
290 
291         } catch (NoSuchAlgorithmException e) { // Gee i'm so polite (pw)
292             throw new NoSuchAlgorithmException("Algorithm not supported!");
293         } catch (NoSuchProviderException e) {
294             throw new RuntimeException("PANIC: Should not happend!");
295         } catch (BadPaddingException e) {
296             throw new RuntimeException("PANIC: This should not happend!");
297         } catch (IllegalBlockSizeException e) {
298             throw new RuntimeException("PANIC: This should not happend!");
299         } catch (InvalidKeySpecException e) {
300             throw new RuntimeException("PANIC: This should not happend!");
301         }
302     }
303 
304 
engineGetKeySize(Key key)305     protected int engineGetKeySize(Key key) throws InvalidKeyException {
306 
307         if(key instanceof RSAPublicKey)
308             return ((RSAPublicKey)key).getModulus().bitLength();
309         else if(key instanceof RSAPrivateKey)
310             return ((RSAPrivateKey)key).getModulus().bitLength();
311         else
312             throw new InvalidKeyException("Not an RSA key.");
313     }
314 
315 
316     /*
317      * Private methods below.
318      *
319      * This is PKCS1 padding as described in the PKCS1 v 1.5
320      * standard section 8 from RSALabs:
321      * EB = 00 || BT || PS || 00 || D.
322      *
323      * But since BigInteger actually removes any leading zero
324      * the encrypted buffer will be without the first 00.
325      *
326      * Both pad and unpad assumes us to have check so that the
327      * output buffer is of valid size.
328      *
329      * I have done so we may use both private and public keys
330      * as input, ie BT may be either 0x00, 0x01 or 0x02. (pw)
331      */
pad(byte [] input, int inputLen, int offset, int bt)332     private byte[] pad(byte [] input, int inputLen, int offset, int bt)
333     throws BadPaddingException
334     {
335         int k = (n.bitLength() + 7)/8;
336         if (inputLen > k-11)
337             throw new BadPaddingException("Data too long for this modulus!");
338 
339         byte [] ed = new byte[k];
340         int padLen = k - 3 - inputLen;
341         ed[0] = ed[2 + padLen] = 0x00;
342 
343         switch (bt) {
344           case 0x00:
345             for (int i = 1; i < (2 + padLen); i++)
346                 ed[i] = 0x00;
347             break;
348           case 0x01:
349             ed[1] = 0x01;
350             for (int i = 2; i < (2 + padLen); i++)
351                 ed[i] = (byte)0xFF;
352             break;
353           case 0x02:
354             ed[1] = 0x02;
355             byte [] b = new byte[1];
356             SecureRandom sr = new SecureRandom();
357             for (int i = 2; i < (2 + padLen); i++) {
358                 b[0] = 0;
359                 while (b[0] == 0)
360                     sr.nextBytes(b);
361                 ed[i] = b[0];
362             }
363             break;
364           default:
365             throw new BadPaddingException("Wrong block type!");
366         }
367 
368         System.arraycopy(input, offset, ed, padLen + 3, inputLen);
369         return ed;
370     }
371 
unpad(byte [] input, int inputLen, int inOffset, byte [] output, int outOffset)372     private int unpad(byte [] input, int inputLen, int inOffset,
373                       byte [] output, int outOffset)
374     throws BadPaddingException {
375         int bt = input[inOffset];
376 
377         int padLen = 1;
378         switch (bt) {
379           case 0x00:
380             for (;; padLen++)
381                 if (input[inOffset + padLen + 1] != (byte)0x00) break;
382             break;
383           case 0x01:
384           case 0x02:
385             for (;; padLen++)
386                 if (input[inOffset + padLen] == (byte)0x00) break;
387             break;
388           default:
389             throw new BadPaddingException("Wrong block type!");
390         }
391         padLen++;
392 
393         int len = inputLen - inOffset - padLen;
394         System.arraycopy(input, inOffset + padLen, output, outOffset, len);
395         return len;
396     }
397 }
398