1 /* $Id: RSACipher_ECB_PKCS1.java,v 1.12 2003/01/15 21:07:51 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.pk;
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 
305     /*
306      * Should we implement this one?? (pw)
307     protected int
308     engineGetKeySize(Key key)
309     throws InvalidKeyException {
310         throw new UnsupportedOperationException(
311             "Not implemented by the provider!");
312     }
313     */
314 
315     /*
316      * Private methods below.
317      *
318      * This is PKCS1 padding as described in the PKCS1 v 1.5
319      * standard section 8 from RSALabs:
320      * EB = 00 || BT || PS || 00 || D.
321      *
322      * But since BigInteger actually removes any leading zero
323      * the encrypted buffer will be without the first 00.
324      *
325      * Both pad and unpad assumes us to have check so that the
326      * output buffer is of valid size.
327      *
328      * I have done so we may use both private and public keys
329      * as input, ie BT may be either 0x00, 0x01 or 0x02. (pw)
330      */
pad(byte [] input, int inputLen, int offset, int bt)331     private byte[] pad(byte [] input, int inputLen, int offset, int bt)
332     throws BadPaddingException
333     {
334         int k = (n.bitLength() + 7)/8;
335         if (inputLen > k-11)
336             throw new BadPaddingException("Data too long for this modulus!");
337 
338         byte [] ed = new byte[k];
339         int padLen = k - 3 - inputLen;
340         ed[0] = ed[2 + padLen] = 0x00;
341 
342         switch (bt) {
343           case 0x00:
344             for (int i = 1; i < (2 + padLen); i++)
345                 ed[i] = 0x00;
346             break;
347           case 0x01:
348             ed[1] = 0x01;
349             for (int i = 2; i < (2 + padLen); i++)
350                 ed[i] = (byte)0xFF;
351             break;
352           case 0x02:
353             ed[1] = 0x02;
354             byte [] b = new byte[1];
355             SecureRandom sr = new SecureRandom();
356             for (int i = 2; i < (2 + padLen); i++) {
357                 b[0] = 0;
358                 while (b[0] == 0)
359                     sr.nextBytes(b);
360                 ed[i] = b[0];
361             }
362             break;
363           default:
364             throw new BadPaddingException("Wrong block type!");
365         }
366 
367         System.arraycopy(input, offset, ed, padLen + 3, inputLen);
368         return ed;
369     }
370 
unpad(byte [] input, int inputLen, int inOffset, byte [] output, int outOffset)371     private int unpad(byte [] input, int inputLen, int inOffset,
372                       byte [] output, int outOffset)
373     throws BadPaddingException {
374         int bt = input[inOffset];
375 
376         int padLen = 1;
377         switch (bt) {
378           case 0x00:
379             for (;; padLen++)
380                 if (input[inOffset + padLen + 1] != (byte)0x00) break;
381             break;
382           case 0x01:
383           case 0x02:
384             for (;; padLen++)
385                 if (input[inOffset + padLen] == (byte)0x00) break;
386             break;
387           default:
388             throw new BadPaddingException("Wrong block type!");
389         }
390         padLen++;
391 
392         int len = inputLen - inOffset - padLen;
393         System.arraycopy(input, inOffset + padLen, output, outOffset, len);
394         return len;
395     }
396 }
397