001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.midp.crypto;
028:
029: /**
030: * This class implements RSA encryption/decryption
031: */
032: public final class RSA extends Cipher {
033: /** Local certificate key. */
034: private RSAKey ckey = null;
035: /** Current cipher mode. */
036: private int mode;
037: /** Local random number for seed. */
038: private static SecureRandom rnd = null;
039:
040: /** Signature pad offset. */
041: private final static int PAD_OFFSET = 2;
042:
043: /** Message to sign. */
044: private byte[] messageToSign;
045:
046: /** Number of bytes in the message to sign. */
047: private int bytesInMessage;
048:
049: /**
050: * A native method for performing modular exponentiation.
051: *
052: * @param data contains the data on which exponentiation is to
053: * be performed
054: * @param exponent contains the exponent, e.g. 65537 (decimal) is
055: * written as a three-byte array containing
056: * 0x01, 0x00, 0x01
057: * @param modulus contains the modulus
058: * @param result the result of the modular exponentiation is
059: * returned in this array
060: * @return length of the result in bytes
061: * @exception IllegalArgumentException if a argument is too long for
062: * the native code to handle. (currently (32K - 8) bits max)
063: */
064: private static native int modExp(byte[] data, byte[] exponent,
065: byte[] modulus, byte[] result)
066: throws IllegalArgumentException;
067:
068: /**
069: * Performs an RSA operation on specified data. If the data length
070: * is not the same as the modulus length (as may happen for an
071: * encryption request), PKCS#1 block type 2 padding is added.
072: * <P />
073: * @param data byte array to be encrypted/decrypted
074: * @return a byte array containing the result
075: * @exception IllegalStateException
076: * if the encryption/decryption key is missing a modulus or
077: * exponent
078: */
079: private byte[] doIt(byte[] data) {
080: int modLen = ckey.getModulusLen();
081: byte[] buf = new byte[modLen];
082: byte[] mod = new byte[modLen];
083: int bufLen;
084:
085: // Note: Both RSAPublicKey and RSAPrivateKey provide the same
086: // interface
087: short val = ckey.getModulus(mod, (short) 0);
088:
089: byte[] tmp = new byte[modLen];
090: val = ckey.getExponent(tmp, (short) 0);
091: byte[] exp = new byte[val];
092: System.arraycopy(tmp, 0, exp, 0, val);
093:
094: bufLen = modExp(data, exp, mod, buf);
095:
096: if (bufLen == modLen) {
097: return buf;
098: } else if (bufLen < modLen) {
099: // Reuse tmp which already points to a byte array of modLen size
100: for (int i = 0; i < modLen; i++)
101: tmp[i] = 0;
102:
103: if (buf[0] == (byte) 0x01) {
104: tmp[0] = (byte) 0x00;
105: tmp[1] = (byte) 0x01;
106: for (int i = 2; i < modLen - bufLen + 1; i++) {
107: tmp[i] = (byte) 0xff;
108: }
109: System.arraycopy(buf, 1, tmp, modLen - bufLen + 1,
110: (bufLen - 1));
111: } else {
112: System
113: .arraycopy(buf, 0, tmp, (modLen - bufLen),
114: bufLen);
115: }
116:
117: return tmp;
118: } else { // bufLen > modLen, key may be too long
119: throw new IllegalArgumentException("Key too long");
120: }
121: }
122:
123: /**
124: * Constructor for RSA.
125: *
126: * @exception RuntimeException if the random number generator can't be
127: * created
128: */
129: public RSA() {
130: mode = Cipher.MODE_UNINITIALIZED;
131:
132: try {
133: rnd = SecureRandom
134: .getInstance(SecureRandom.ALG_SECURE_RANDOM);
135: } catch (NoSuchAlgorithmException e) {
136: throw new RuntimeException(
137: "Random number generator missing");
138: }
139: }
140:
141: /**
142: * Called by the factory method to set the mode and padding parameters.
143: * Need because Class.newInstance does not take args.
144: *
145: * @param mode the mode parsed from the transformation parameter of
146: * getInstance and upper cased
147: * @param padding the padding parsed from the transformation parameter of
148: * getInstance and upper cased
149: *
150: * @exception NoSuchPaddingException if <code>transformation</code>
151: * contains a padding scheme that is not available.
152: * @exception IllegalArgumentException if mode is incorrect
153: */
154: protected void setChainingModeAndPadding(String mode, String padding)
155: throws NoSuchPaddingException {
156:
157: if (!(mode.equals("") || mode.equals("NONE"))) {
158: throw new IllegalArgumentException("illegal chaining mode");
159: }
160:
161: // NOPADDING is not an option.
162: if (!(padding.equals("") || padding.equals("PKCS1PADDING"))) {
163: throw new NoSuchPaddingException();
164: }
165: }
166:
167: /**
168: * Initializes this cipher with a key and a set of algorithm
169: * parameters.
170: *
171: * @param opMode the operation mode of this cipher
172: * @param key the encryption key
173: * @param params the algorithm parameters
174: *
175: * @exception java.security.InvalidKeyException if the given key
176: * is inappropriate for initializing this cipher
177: * @exception java.security.InvalidAlgorithmParameterException
178: * if the given algorithm parameters are inappropriate for this cipher
179: * @exception IllegalArgumentException if opMode is incorrect
180: */
181: public void init(int opMode, Key key, CryptoParameter params)
182: throws InvalidKeyException,
183: InvalidAlgorithmParameterException {
184:
185: if (!(key instanceof RSAKey)) {
186: throw new InvalidKeyException();
187: }
188:
189: if (opMode != DECRYPT_MODE && opMode != ENCRYPT_MODE) {
190: throw new IllegalArgumentException("Wrong operation mode");
191: }
192:
193: mode = opMode;
194: ckey = (RSAKey) key;
195:
196: if (ckey.getModulusLen() == 0) {
197: throw new InvalidKeyException();
198: }
199:
200: messageToSign = new byte[ckey.getModulusLen()];
201: bytesInMessage = 0;
202: }
203:
204: /**
205: * Fills the internal buffer to be encrypted or decrypted
206: * (depending on how this cipher was initialized).
207: * For the RSA public key cipher there is no output until doFinal.
208: *
209: * @param inBuf the input buffer
210: * @param inOff the offset in <code>input</code> where the input
211: * starts
212: * @param inLen the input length
213: * @param outBuf the buffer for the result
214: * @param outOff the offset in <code>output</code> where the result
215: * is stored
216: *
217: * @return the number of bytes stored in <code>output</code>
218: *
219: * @exception IllegalStateException if this cipher is in a wrong state
220: * (e.g., has not been initialized)
221: * @exception ShortBufferException if the given output buffer is too small
222: * to hold the result
223: * @exception IllegalArgumentException if a length or offset is incorrect
224: */
225: public int update(byte inBuf[], int inOff, int inLen,
226: byte outBuf[], int outOff) throws IllegalStateException,
227: ShortBufferException {
228:
229: addToMessage(inBuf, inOff, inLen);
230: return 0;
231: }
232:
233: /**
234: * Fills the internal message buffer to be encrypted or decrypted
235: * (depending on how this cipher was initialized).
236: * For the RSA public key cipher there is no output until doFinal.
237: *
238: * @param inBuf the input buffer
239: * @param inOff the offset in <code>input</code> where the input
240: * starts
241: * @param inLen the input length
242: *
243: * @exception IllegalStateException if this cipher is in a wrong state
244: * (e.g., has not been initialized)
245: * @exception IllegalArgumentException if a length or offset is incorrect
246: */
247: private void addToMessage(byte inBuf[], int inOff, int inLen)
248: throws IllegalStateException {
249: int bytesToCopy;
250:
251: if (mode == Cipher.MODE_UNINITIALIZED) {
252: throw new IllegalStateException();
253: }
254:
255: if (inLen == 0) {
256: return;
257: }
258:
259: if (inBuf == null || inOff < 0 || inLen < 0
260: || inOff + inLen > inBuf.length) {
261: throw new IllegalArgumentException("input out of bounds");
262: }
263:
264: bytesToCopy = messageToSign.length - bytesInMessage;
265: if (inLen < bytesToCopy) {
266: bytesToCopy = inLen;
267: }
268:
269: System.arraycopy(inBuf, inOff, messageToSign, bytesInMessage,
270: bytesToCopy);
271: bytesInMessage += bytesToCopy;
272: }
273:
274: /**
275: * Performs the crypto process the buffer.
276: *
277: * @param inBuf the input buffer
278: * @param inOff the offset in <code>input</code> where the input
279: * starts
280: * @param inLen the input length
281: * @param outBuf the buffer for the result
282: * @param outOff the offset in <code>output</code> where the result
283: * is stored
284: *
285: * @return the number of bytes stored in <code>output</code>
286: *
287: * @exception IllegalStateException if this cipher is in a wrong state
288: * (e.g., has not been initialized)
289: * @exception IllegalBlockSizeException if this cipher is a block cipher,
290: * no padding has been requested (only in encryption mode), and the total
291: * input length of the data processed by this cipher is not a multiple of
292: * block size
293: * @exception ShortBufferException if the given output buffer is too small
294: * to hold the result
295: * @exception BadPaddingException if this cipher is in decryption mode,
296: * and (un)padding has been requested, but the decrypted data is not
297: * bounded by the appropriate padding bytes
298: * @exception IllegalArgumentException if input is greater than the
299: * cipher with the given key can handle
300: */
301: private int performRsa(byte inBuf[], int inOff, int inLen,
302: byte outBuf[], int outOff) throws IllegalStateException,
303: ShortBufferException, IllegalBlockSizeException,
304: BadPaddingException {
305: int modLen;
306: int outLen;
307: byte[] tmp;
308: byte[] res;
309: int padLen;
310: int endOfPad;
311:
312: modLen = ckey.getModulusLen();
313:
314: switch (mode) {
315: case Cipher.ENCRYPT_MODE:
316: if (inLen > modLen - 11) {
317: throw new IllegalArgumentException("Too much input");
318: }
319:
320: /*
321: * Add PKCS#1 (ver 1.5) padding
322: * 0x00 | 0x02 | <random, non-zero pad bytes> | 0x00 | <data>
323: */
324: tmp = new byte[modLen];
325: tmp[0] = (byte) 0x00;
326:
327: padLen = modLen - inLen - 3;
328: endOfPad = padLen + PAD_OFFSET;
329:
330: if (ckey instanceof RSAPublicKey) {
331: tmp[1] = (byte) 0x02; // for block type 02
332:
333: // Use random padding (replacing 0x00s)
334: rnd.nextBytes(tmp, PAD_OFFSET, padLen);
335:
336: for (int i = PAD_OFFSET; i < endOfPad; i++) {
337: if (tmp[i] == (byte) 0x00) {
338: // padding byte must be non-zero
339: tmp[i] = (byte) 0xff;
340: }
341: }
342: } else {
343: // NOTE: RFC2313 suggests 0x01 for private key signatures
344: tmp[1] = (byte) 0x01;
345: for (int i = PAD_OFFSET; i < endOfPad; i++) {
346: tmp[i] = (byte) 0xff;
347: }
348: }
349:
350: if (inLen > modLen) {
351: throw new IllegalArgumentException("inlen > modlen");
352: }
353:
354: tmp[modLen - inLen - 1] = (byte) 0x00;
355: System.arraycopy(inBuf, inOff, tmp, modLen - inLen, inLen);
356:
357: res = doIt(tmp);
358:
359: if (outOff + res.length > outBuf.length) {
360: throw new ShortBufferException();
361: }
362:
363: System.arraycopy(res, 0, outBuf, outOff, res.length);
364: outLen = res.length;
365: break;
366:
367: case Cipher.DECRYPT_MODE:
368: // This is specified in RFC2313
369: if (inLen != modLen) {
370: throw new IllegalArgumentException("inlen != modlen");
371: }
372:
373: if (inOff != 0) {
374: tmp = new byte[modLen];
375: System.arraycopy(inBuf, inOff, tmp, 0, modLen);
376: res = doIt(tmp);
377: } else {
378: res = doIt(inBuf);
379: }
380:
381: // Count number of padding bytes (these must be non-zero)
382: padLen = 0;
383: for (int i = 2; (i < res.length) && (res[i] != (byte) 0x00); i++) {
384: padLen++;
385: }
386:
387: /*
388: * Note that whatever our decryption key type is,
389: * the other side used an opposite key type when encrypting
390: * so if our key type is TYPE_RSA_PUBLIC, the sender used
391: * TYPE_RSA_PRIVATE and the expected block type after
392: * decryption (before encryption) is 0x00 or 0x01
393: */
394: if ((padLen < modLen - 3)
395: && (res.length > 1)
396: && (res[0] == (byte) 0x00)
397: && (((ckey instanceof RSAPublicKey) && ((res[1] == (byte) 0x01) || (res[1] == (byte) 0x00))) || ((ckey instanceof RSAPrivateKey) && (res[1] == (byte) 0x02)))) {
398: outLen = modLen - padLen - 3;
399:
400: if (outOff + outLen > outBuf.length) {
401: throw new ShortBufferException();
402: }
403:
404: System.arraycopy(res, padLen + 3, outBuf, outOff,
405: outLen);
406: } else {
407: throw new BadPaddingException();
408: }
409:
410: break;
411:
412: default:
413: throw new IllegalStateException();
414: }
415:
416: return outLen;
417: }
418:
419: /**
420: * Process the final data record.
421: *
422: * @param inBuf input buffer of data
423: * @param inOff offset in the provided input buffer
424: * @param inLen length of data to be processed
425: * @param outBuf output buffer of data
426: * @param outOff offset in the provided output buffer
427: * @return number of bytes copied to output buffer
428: *
429: * @exception IllegalStateException if this cipher is in a wrong state
430: * (e.g., has not been initialized)
431: * @exception IllegalBlockSizeException if this cipher is a block cipher,
432: * no padding has been requested (only in encryption mode), and the total
433: * input length of the data processed by this cipher is not a multiple of
434: * block size
435: * @exception ShortBufferException if the given output buffer is too small
436: * to hold the result
437: * @exception BadPaddingException if this cipher is in decryption mode,
438: * and (un)padding has been requested, but the decrypted data is not
439: * bounded by the appropriate padding bytes
440: * @exception IllegalArgumentException if input is greater than the
441: * cipher with the given key can handle, or the output
442: * parameters are invalid
443: */
444: public int doFinal(byte[] inBuf, int inOff, int inLen,
445: byte[] outBuf, int outOff) throws IllegalStateException,
446: ShortBufferException, IllegalBlockSizeException,
447: BadPaddingException {
448:
449: addToMessage(inBuf, inOff, inLen);
450:
451: if (outBuf == null || outOff < 0) {
452: throw new IllegalArgumentException("output out of bounds");
453: }
454:
455: int val = performRsa(messageToSign, 0, bytesInMessage, outBuf,
456: outOff);
457:
458: try {
459: init(mode, ckey);
460: } catch (InvalidKeyException ike) {
461: // Ignore, nothing to do
462: }
463:
464: return val;
465: }
466: }
|