FileDocCategorySizeDatePackage
RSA.javaAPI DocphoneME MR2 API (J2ME)16799Wed May 02 18:00:00 BST 2007com.sun.midp.crypto

RSA

public final class RSA extends Cipher
This class implements RSA encryption/decryption

Fields Summary
private RSAKey
ckey
Local certificate key.
private int
mode
Current cipher mode.
private static SecureRandom
rnd
Local random number for seed.
private static final int
PAD_OFFSET
Signature pad offset.
private byte[]
messageToSign
Message to sign.
private int
bytesInMessage
Number of bytes in the message to sign.
Constructors Summary
public RSA()
Constructor for RSA.

exception
RuntimeException if the random number generator can't be created

        mode = Cipher.MODE_UNINITIALIZED;

        try {
            rnd = SecureRandom.getInstance(SecureRandom.ALG_SECURE_RANDOM);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Random number generator missing");
        }
    
Methods Summary
private voidaddToMessage(byte[] inBuf, int inOff, int inLen)
Fills the internal message buffer to be encrypted or decrypted (depending on how this cipher was initialized). For the RSA public key cipher there is no output until doFinal.

param
inBuf the input buffer
param
inOff the offset in input where the input starts
param
inLen the input length
exception
IllegalStateException if this cipher is in a wrong state (e.g., has not been initialized)
exception
IllegalArgumentException if a length or offset is incorrect

        int bytesToCopy;

        if (mode == Cipher.MODE_UNINITIALIZED) {
            throw new IllegalStateException();
        }

        if (inLen == 0) {
            return;
        }

        if (inBuf == null || inOff < 0 || inLen < 0 ||
                inOff + inLen > inBuf.length) {
            throw new IllegalArgumentException("input out of bounds");
        }

        bytesToCopy = messageToSign.length - bytesInMessage;
        if (inLen < bytesToCopy) {
            bytesToCopy = inLen;
        }

        System.arraycopy(inBuf, inOff, messageToSign, bytesInMessage,
                         bytesToCopy);
        bytesInMessage += bytesToCopy;
    
public intdoFinal(byte[] inBuf, int inOff, int inLen, byte[] outBuf, int outOff)
Process the final data record.

param
inBuf input buffer of data
param
inOff offset in the provided input buffer
param
inLen length of data to be processed
param
outBuf output buffer of data
param
outOff offset in the provided output buffer
return
number of bytes copied to output buffer
exception
IllegalStateException if this cipher is in a wrong state (e.g., has not been initialized)
exception
IllegalBlockSizeException if this cipher is a block cipher, no padding has been requested (only in encryption mode), and the total input length of the data processed by this cipher is not a multiple of block size
exception
ShortBufferException if the given output buffer is too small to hold the result
exception
BadPaddingException if this cipher is in decryption mode, and (un)padding has been requested, but the decrypted data is not bounded by the appropriate padding bytes
exception
IllegalArgumentException if input is greater than the cipher with the given key can handle, or the output parameters are invalid


        addToMessage(inBuf, inOff, inLen);

        if (outBuf == null || outOff < 0) {
            throw new IllegalArgumentException("output out of bounds");
        }

        int val = performRsa(messageToSign, 0, bytesInMessage, outBuf, outOff);

        try {
            init(mode, ckey);
        } catch (InvalidKeyException ike) {
            // Ignore, nothing to do
        }

        return val;
    
private byte[]doIt(byte[] data)
Performs an RSA operation on specified data. If the data length is not the same as the modulus length (as may happen for an encryption request), PKCS#1 block type 2 padding is added.

param
data byte array to be encrypted/decrypted
return
a byte array containing the result
exception
IllegalStateException if the encryption/decryption key is missing a modulus or exponent


                                                                                                                                                                                         
           
                                         
         

                                                                                               
        
        int modLen = ckey.getModulusLen();
        byte[] buf = new byte[modLen];
        byte[] mod = new byte[modLen];
        int bufLen;
        
        // Note: Both RSAPublicKey and RSAPrivateKey provide the same
        // interface
        short val = ckey.getModulus(mod, (short) 0);
        
        byte[] tmp = new byte[modLen];
        val = ckey.getExponent(tmp, (short) 0);
        byte[] exp = new byte [val];
        System.arraycopy(tmp, 0, exp, 0, val);
        
        bufLen = modExp(data, exp, mod, buf);

        if (bufLen == modLen) {
            return buf;
        } else if (bufLen < modLen) {
            // Reuse tmp which already points to a byte array of modLen size
            for (int i = 0; i < modLen; i++) tmp[i] = 0;
            
            if (buf[0] == (byte) 0x01) {
                tmp[0] = (byte) 0x00;
                tmp[1] = (byte) 0x01;
                for (int i = 2; i < modLen - bufLen + 1; i++) {
                    tmp[i] = (byte) 0xff;
                }
                System.arraycopy(buf, 1, tmp, 
                                 modLen - bufLen + 1, (bufLen - 1));
            } else {
                System.arraycopy(buf, 0, tmp, (modLen - bufLen), bufLen);
            }

            return tmp;
        } else {  // bufLen > modLen, key may be too long
            throw new IllegalArgumentException("Key too long");
        }
    
public voidinit(int opMode, Key key, CryptoParameter params)
Initializes this cipher with a key and a set of algorithm parameters.

param
opMode the operation mode of this cipher
param
key the encryption key
param
params the algorithm parameters
exception
java.security.InvalidKeyException if the given key is inappropriate for initializing this cipher
exception
java.security.InvalidAlgorithmParameterException if the given algorithm parameters are inappropriate for this cipher
exception
IllegalArgumentException if opMode is incorrect


        if (!(key instanceof RSAKey)) {
            throw new InvalidKeyException();
        }

        if (opMode != DECRYPT_MODE && opMode != ENCRYPT_MODE) {
            throw new IllegalArgumentException("Wrong operation mode");
        }

        mode = opMode;
        ckey = (RSAKey)key;

        if (ckey.getModulusLen() == 0) {
            throw new InvalidKeyException();
        }

        messageToSign = new byte[ckey.getModulusLen()];
        bytesInMessage = 0;
    
private static native intmodExp(byte[] data, byte[] exponent, byte[] modulus, byte[] result)
A native method for performing modular exponentiation.

param
data contains the data on which exponentiation is to be performed
param
exponent contains the exponent, e.g. 65537 (decimal) is written as a three-byte array containing 0x01, 0x00, 0x01
param
modulus contains the modulus
param
result the result of the modular exponentiation is returned in this array
return
length of the result in bytes
exception
IllegalArgumentException if a argument is too long for the native code to handle. (currently (32K - 8) bits max)

private intperformRsa(byte[] inBuf, int inOff, int inLen, byte[] outBuf, int outOff)
Performs the crypto process the buffer.

param
inBuf the input buffer
param
inOff the offset in input where the input starts
param
inLen the input length
param
outBuf the buffer for the result
param
outOff the offset in output where the result is stored
return
the number of bytes stored in output
exception
IllegalStateException if this cipher is in a wrong state (e.g., has not been initialized)
exception
IllegalBlockSizeException if this cipher is a block cipher, no padding has been requested (only in encryption mode), and the total input length of the data processed by this cipher is not a multiple of block size
exception
ShortBufferException if the given output buffer is too small to hold the result
exception
BadPaddingException if this cipher is in decryption mode, and (un)padding has been requested, but the decrypted data is not bounded by the appropriate padding bytes
exception
IllegalArgumentException if input is greater than the cipher with the given key can handle

        int modLen;
        int outLen;
        byte[] tmp;
        byte[] res;
        int padLen;
        int endOfPad;
                            
        modLen = ckey.getModulusLen();

        switch (mode) {
        case Cipher.ENCRYPT_MODE:
            if (inLen > modLen - 11) {
                throw new IllegalArgumentException("Too much input");
            }

            /*
             * Add PKCS#1 (ver 1.5) padding
             * 0x00 | 0x02 | <random, non-zero pad bytes> | 0x00 | <data>
             */ 
            tmp = new byte[modLen];
            tmp[0] = (byte) 0x00;

            padLen = modLen - inLen - 3;
            endOfPad = padLen + PAD_OFFSET;

            if (ckey instanceof RSAPublicKey) {
                tmp[1] = (byte) 0x02;  // for block type 02

                // Use random padding (replacing 0x00s)
                rnd.nextBytes(tmp, PAD_OFFSET, padLen);
                
                for (int i = PAD_OFFSET; i < endOfPad; i++) {
                    if (tmp[i] == (byte) 0x00) {
                        // padding byte must be non-zero
                        tmp[i] = (byte) 0xff;
                    }
                }
            } else {
                // NOTE: RFC2313 suggests 0x01 for private key signatures
                tmp[1] = (byte) 0x01; 
                for (int i = PAD_OFFSET; i < endOfPad; i++) {
                    tmp[i] = (byte) 0xff;
                }
            }

            if (inLen > modLen) {
                throw new IllegalArgumentException("inlen > modlen");
            }                

            tmp[modLen - inLen - 1] = (byte) 0x00;
            System.arraycopy(inBuf, inOff, tmp, modLen - inLen, inLen);

            res = doIt(tmp);

            if (outOff + res.length > outBuf.length) {
                throw new ShortBufferException();
            }

            System.arraycopy(res, 0, outBuf, outOff, res.length);
            outLen = res.length;
            break;
            
        case Cipher.DECRYPT_MODE:
            // This is specified in RFC2313
            if (inLen != modLen) {
                throw new IllegalArgumentException("inlen != modlen");
            }

            if (inOff != 0) {
                tmp = new byte[modLen];
                System.arraycopy(inBuf, inOff, tmp, 0, modLen);
                res = doIt(tmp);
            } else {
                res = doIt(inBuf);
            }
            
            // Count number of padding bytes (these must be non-zero)
            padLen = 0;
            for (int i = 2; (i < res.length) && (res[i] != (byte) 0x00); i++) {
                padLen++;
            }

            /*
             * Note that whatever our decryption key type is,
             * the other side used an opposite key type when encrypting
             * so if our key type is TYPE_RSA_PUBLIC, the sender used
             * TYPE_RSA_PRIVATE and the expected block type after 
             * decryption (before encryption) is 0x00 or 0x01
             */ 
            if ((padLen < modLen - 3) && (res.length > 1) &&
                (res[0] == (byte) 0x00) &&
                (((ckey instanceof RSAPublicKey) &&
                  ((res[1] == (byte) 0x01) || (res[1] == (byte) 0x00))) ||
                 ((ckey instanceof RSAPrivateKey) && 
                  (res[1] == (byte) 0x02)))) {
                outLen = modLen - padLen - 3;

                if (outOff + outLen > outBuf.length) {
                    throw new ShortBufferException();
                }

                System.arraycopy(res, padLen + 3, outBuf, outOff, outLen);
            } else {
                throw new BadPaddingException();
            }

            break;

        default:
            throw new IllegalStateException();
        }
                            
        return outLen;
    
protected voidsetChainingModeAndPadding(java.lang.String mode, java.lang.String padding)
Called by the factory method to set the mode and padding parameters. Need because Class.newInstance does not take args.

param
mode the mode parsed from the transformation parameter of getInstance and upper cased
param
padding the padding parsed from the transformation parameter of getInstance and upper cased
exception
NoSuchPaddingException if transformation contains a padding scheme that is not available.
exception
IllegalArgumentException if mode is incorrect


        if (!(mode.equals("") || mode.equals("NONE"))) {
            throw new IllegalArgumentException("illegal chaining mode");
        }

        // NOPADDING is not an option.
        if (!(padding.equals("") || padding.equals("PKCS1PADDING"))) {
            throw new NoSuchPaddingException();
        }
    
public intupdate(byte[] inBuf, int inOff, int inLen, byte[] outBuf, int outOff)
Fills the internal buffer to be encrypted or decrypted (depending on how this cipher was initialized). For the RSA public key cipher there is no output until doFinal.

param
inBuf the input buffer
param
inOff the offset in input where the input starts
param
inLen the input length
param
outBuf the buffer for the result
param
outOff the offset in output where the result is stored
return
the number of bytes stored in output
exception
IllegalStateException if this cipher is in a wrong state (e.g., has not been initialized)
exception
ShortBufferException if the given output buffer is too small to hold the result
exception
IllegalArgumentException if a length or offset is incorrect


        addToMessage(inBuf, inOff, inLen);
        return 0;