001: /*
002: * @(#)Alg2.java 1.11 02/06/27
003: *
004: * Copyright (c) 2000-2001 Sun Microsystems, Inc., 901 San Antonio Road,
005: * Palo Alto, CA 94303, U.S.A. All Rights Reserved.
006: *
007: * Sun Microsystems, Inc. has intellectual property rights relating
008: * to the technology embodied in this software. In particular, and
009: * without limitation, these intellectual property rights may include
010: * one or more U.S. patents, foreign patents, or pending
011: * applications. Sun, Sun Microsystems, the Sun logo, Java, KJava,
012: * and all Sun-based and Java-based marks are trademarks or
013: * registered trademarks of Sun Microsystems, Inc. in the United
014: * States and other countries.
015: *
016: * This software is distributed under licenses restricting its use,
017: * copying, distribution, and decompilation. No part of this
018: * software may be reproduced in any form by any means without prior
019: * written authorization of Sun and its licensors, if any.
020: *
021: * FEDERAL ACQUISITIONS: Commercial Software -- Government Users
022: * Subject to Standard License Terms and Conditions
023: */
024: package com.sun.portal.kssl;
025:
026: import com.sun.portal.ksecurity.RSAPublicKey;
027: import com.sun.portal.ksecurity.Key;
028: import com.sun.portal.ksecurity.KeyBuilder;
029: import com.sun.portal.ksecurity.CryptoException;
030: import com.sun.portal.ksecurity.RandomData;
031: import java.math.BigInteger;
032:
033: /**
034: * This class implements RSA encryption/decryption
035: */
036: final class Alg2 extends Cipher {
037: /** Local certificate key. */
038: private Key ckey = null;
039: /** Current cipher mode. */
040: private byte mode = Cipher.MODE_UNKNOWN;
041: /** Local random number for seed. */
042: private static RandomData rnd = null;
043:
044: /** Signature pad offset. */
045: private final static int PAD_OFFSET = 2;
046:
047: /**
048: * A native method for performing modular exponentiation.
049: *
050: * @param data contains the data on which exponentiation is to
051: * be performed
052: * @param exponent contains the exponent, e.g. 65537 (decimal) is
053: * written as a three-byte array containing
054: * 0x01, 0x00, 0x01
055: * @param modulus contains the modulus
056: * @param result the result of the modular exponentiation is
057: * returned in this array
058: * @return length of the result in bytes
059: * @exception IllegalArgumentException if a argument is too long for
060: * the native code to handle. (currently (32K - 8) bits max)
061: */
062: private static int modExp(byte[] data, byte[] exponent,
063: byte[] modulus, byte[] result)
064: throws IllegalArgumentException {
065: BigInteger A = new BigInteger(1, data);
066: BigInteger E = new BigInteger(1, exponent);
067: BigInteger p = new BigInteger(1, modulus);
068: //(a^e)mod p
069: BigInteger resultBig = A.modPow(E, p);
070: byte[] temp = bigInt2Bytes(resultBig);
071: System.arraycopy(temp, 0, result, 0, temp.length);
072: return temp.length;
073:
074: }
075:
076: private static byte[] bigInt2Bytes(BigInteger big) {
077:
078: byte[] bigBytes = big.toByteArray();
079: // If bitlength is a multiple of 8, the sign will
080: // take up its own extra byte. All values are assumed
081: // to be positive and we never copy the sign byte
082: boolean extraSignByte = ((big.bitLength() % 8) == 0);
083: int byteSize = extraSignByte ? (big.bitLength() / 8) : ((big
084: .bitLength() / 8) + 1);
085:
086: byte[] res = new byte[byteSize];
087: System.arraycopy(bigBytes, (extraSignByte ? 1 : 0), res, 0,
088: byteSize);
089: return res;
090: }
091:
092: /**
093: * Performs an RSA operation on specified data. If the data length
094: * is not the same as the modulus length (as may happen for an
095: * encryption request), PKCS#1 block type 2 padding is added.
096: * <P />
097: * @param data byte array to be encrypted/decrypted
098: * @return a byte array containing the result
099: * @exception CryptoException with the UNINITIALIZED_KEY reason code
100: * if the encryption/decryption key is missing a modulus or
101: * exponent
102: */
103: private byte[] doIt(byte[] data) throws CryptoException {
104: int modLen = ckey.getSize() >>> 3;
105: byte[] buf = new byte[modLen];
106: byte[] mod = new byte[modLen];
107: int bufLen;
108:
109: // Note: Both RSAPublicKey and RSAPrivateKey provide the same
110: // interface
111: short val = ((RSAPublicKey) ckey).getModulus(mod, (short) 0);
112: if (val == 0)
113: throw new CryptoException(CryptoException.UNINITIALIZED_KEY);
114:
115: byte[] tmp = new byte[modLen];
116: val = ((RSAPublicKey) ckey).getExponent(tmp, (short) 0);
117: if (val == 0)
118: throw new CryptoException(CryptoException.UNINITIALIZED_KEY);
119: byte[] exp = new byte[val];
120: System.arraycopy(tmp, 0, exp, 0, val);
121:
122: bufLen = modExp(data, exp, mod, buf);
123: if (bufLen == modLen) {
124: return buf;
125: } else if (bufLen < modLen) {
126: // Reuse tmp which already points to a byte array of modLen size
127: for (int i = 0; i < modLen; i++)
128: tmp[i] = 0;
129:
130: if (buf[0] == (byte) 0x01) {
131: tmp[0] = (byte) 0x00;
132: tmp[1] = (byte) 0x01;
133: for (int i = 2; i < modLen - bufLen + 1; i++) {
134: tmp[i] = (byte) 0xff;
135: }
136: System.arraycopy(buf, 1, tmp, modLen - bufLen + 1,
137: (bufLen - 1));
138: } else {
139: System
140: .arraycopy(buf, 0, tmp, (modLen - bufLen),
141: bufLen);
142: }
143:
144: return tmp;
145: } else { // bufLen > modLen, key may be too long
146: throw new CryptoException(CryptoException.ILLEGAL_VALUE);
147: }
148: }
149:
150: /**
151: * Constructor for algorithm 2.
152: */
153: public void Alg2() {
154: mode = Cipher.MODE_UNKNOWN;
155: }
156:
157: /**
158: * Get the alorithm (ALG_RSA_PKCS1).
159: * @return algorithm 2 code
160: */
161: public byte getAlgorithm() {
162: return Cipher.ALG_RSA_PKCS1;
163: }
164:
165: /**
166: * Initialize the alorithm with initial data. (Not supported)
167: * @param theKey public key
168: * @param theMode mode of operation
169: * @param b buffer of data
170: * @param off offset in the provided buffer
171: * @param len length of data to be processed
172: * @exception CryptoException is thrown, because the method is
173: * not supported.
174: */
175: public void init(Key theKey, byte theMode, byte[] b, int off,
176: int len) throws CryptoException {
177: throw new CryptoException(CryptoException.ILLEGAL_USE);
178: }
179:
180: /**
181: * Initialize the alorithm.
182: * @param theKey public key
183: * @param theMode mode of operation
184: * @exception CryptoException is thrown, if there is an error
185: * in the arguments.
186: */
187: public void init(Key theKey, byte theMode) throws CryptoException {
188: if (((theKey.getType() != KeyBuilder.TYPE_RSA_PRIVATE) && (theKey
189: .getType() != KeyBuilder.TYPE_RSA_PUBLIC))
190: || ((theMode != Cipher.MODE_ENCRYPT) && (theMode != Cipher.MODE_DECRYPT))) {
191: throw new CryptoException(CryptoException.ILLEGAL_VALUE);
192: }
193:
194: if (rnd == null) {
195: rnd = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
196: }
197:
198: mode = theMode;
199: ckey = theKey;
200: }
201:
202: /**
203: * Update the current data record.
204: *
205: * @param inBuf input buffer of data
206: * @param inOff offset in the provided input buffer
207: * @param inLen length of data to be processed
208: * @param outBuf output buffer of data
209: * @param outOff offset in the provided output buffer
210: * @return number of bytes copied to output buffer
211: * @exception CryptoException is thrown, if there are errors
212: * in the arguments
213: */
214: public int update(byte[] inBuf, int inOff, int inLen,
215: byte[] outBuf, int outOff) throws CryptoException {
216:
217: int modLen;
218: int val;
219: byte[] tmp;
220: byte[] res;
221: int padLen;
222: int endOfPad;
223: byte keyType;
224:
225: if (ckey == null) {
226: throw new CryptoException(CryptoException.UNINITIALIZED_KEY);
227: }
228:
229: if (mode == Cipher.MODE_UNKNOWN) {
230: throw new CryptoException(CryptoException.INVALID_INIT);
231: }
232:
233: modLen = ckey.getSize() >>> 3;
234:
235: if ((inLen < 0) || (inOff + inLen > inBuf.length)
236: || (outOff + modLen > outBuf.length)) {
237: throw new CryptoException(CryptoException.ILLEGAL_USE);
238: }
239:
240: keyType = ckey.getType();
241:
242: switch (mode) {
243: case Cipher.MODE_ENCRYPT:
244: if (inLen > modLen - 11) {
245: throw new CryptoException(CryptoException.ILLEGAL_USE);
246: }
247:
248: /*
249: * Add PKCS#1 (ver 1.5) padding
250: * 0x00 | 0x02 | <random, non-zero pad bytes> | 0x00 | <data>
251: */
252: tmp = new byte[modLen];
253: tmp[0] = (byte) 0x00;
254:
255: padLen = modLen - inLen - 3;
256: endOfPad = padLen + PAD_OFFSET;
257:
258: if (keyType == KeyBuilder.TYPE_RSA_PUBLIC) {
259: tmp[1] = (byte) 0x02; // for block type 02
260:
261: // Use random padding (replacing 0x00s)
262: rnd.generateData(tmp, (short) PAD_OFFSET,
263: (short) padLen);
264:
265: for (int i = PAD_OFFSET; i < endOfPad; i++) {
266: if (tmp[i] == (byte) 0x00) {
267: // padding byte must be non-zero
268: tmp[i] = (byte) 0xff;
269: }
270: }
271: } else {
272: // NOTE: RFC2313 suggests 0x01 for private key signatures
273: tmp[1] = (byte) 0x01;
274: for (int i = PAD_OFFSET; i < endOfPad; i++) {
275: tmp[i] = (byte) 0xff;
276: }
277: }
278:
279: tmp[modLen - inLen - 1] = (byte) 0x00;
280: System.arraycopy(inBuf, inOff, tmp, modLen - inLen, inLen);
281:
282: res = doIt(tmp);
283:
284: System.arraycopy(res, 0, outBuf, outOff, res.length);
285: val = res.length;
286: break;
287:
288: case Cipher.MODE_DECRYPT:
289: // This is specified in RFC2313
290: if (inLen != modLen) {
291: throw new CryptoException(CryptoException.ILLEGAL_USE);
292: }
293:
294: if (inOff != 0) {
295: tmp = new byte[modLen];
296: System.arraycopy(inBuf, inOff, tmp, 0, modLen);
297: res = doIt(tmp);
298: } else {
299: res = doIt(inBuf);
300: }
301:
302: // Count number of padding bytes (these must be non-zero)
303: padLen = 0;
304: for (int i = 2; (res[i] != (byte) 0x00) && (i < res.length); i++)
305: padLen++;
306:
307: /*
308: * Note that whatever our decryption key type is,
309: * the other side used an opposite key type when encrypting
310: * so if our key type is TYPE_RSA_PUBLIC, the sender used
311: * TYPE_RSA_PRIVATE and the expected block type after
312: * decryption (before encryption) is 0x00 or 0x01
313: */
314: if ((padLen < modLen - 3)
315: && (res[0] == (byte) 0x00)
316: && (((keyType == KeyBuilder.TYPE_RSA_PUBLIC) && ((res[1] == (byte) 0x01) || (res[1] == (byte) 0x00))) || ((keyType == KeyBuilder.TYPE_RSA_PRIVATE) && (res[1] == (byte) 0x02)))) {
317: val = modLen - padLen - 3;
318: System.arraycopy(res, padLen + 3, outBuf, outOff, val);
319: } else {
320: throw new CryptoException(
321: CryptoException.ILLEGAL_ENCODING);
322: }
323:
324: break;
325:
326: default:
327: throw new CryptoException(CryptoException.ILLEGAL_VALUE);
328: }
329:
330: return val;
331: }
332:
333: /**
334: * Process the final data record.
335: *
336: * @param inBuf input buffer of data
337: * @param inOff offset in the provided input buffer
338: * @param inLen length of data to be processed
339: * @param outBuf output buffer of data
340: * @param outOff offset in the provided output buffer
341: * @return number of bytes copied to output buffer
342: * @exception CryptoException is thrown, if there are errors
343: * in the arguments
344: */
345: public int doFinal(byte[] inBuf, int inOff, int inLen,
346: byte[] outBuf, int outOff) throws CryptoException {
347: int val = update(inBuf, inOff, inLen, outBuf, outOff);
348: init(ckey, mode);
349: return val;
350: }
351: }
|