Source for gnu.javax.crypto.RSACipherImpl

   1: /* RSACipherImpl.java --
   2:    Copyright (C) 2005, 2006  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package gnu.javax.crypto;
  40: 
  41: import gnu.classpath.debug.Component;
  42: import gnu.classpath.debug.SystemLogger;
  43: import gnu.java.security.util.ByteArray;
  44: 
  45: import java.math.BigInteger;
  46: import java.security.AlgorithmParameters;
  47: import java.security.InvalidKeyException;
  48: import java.security.Key;
  49: import java.security.NoSuchAlgorithmException;
  50: import java.security.SecureRandom;
  51: import java.security.interfaces.RSAKey;
  52: import java.security.interfaces.RSAPrivateCrtKey;
  53: import java.security.interfaces.RSAPrivateKey;
  54: import java.security.interfaces.RSAPublicKey;
  55: import java.security.spec.AlgorithmParameterSpec;
  56: import java.util.logging.Logger;
  57: 
  58: import javax.crypto.BadPaddingException;
  59: import javax.crypto.Cipher;
  60: import javax.crypto.CipherSpi;
  61: import javax.crypto.IllegalBlockSizeException;
  62: import javax.crypto.NoSuchPaddingException;
  63: import javax.crypto.ShortBufferException;
  64: 
  65: public class RSACipherImpl
  66:     extends CipherSpi
  67: {
  68:   private static final Logger logger = SystemLogger.SYSTEM;
  69:   private static final byte[] EMPTY = new byte[0];
  70:   private int opmode = -1;
  71:   private RSAPrivateKey decipherKey = null;
  72:   private RSAPublicKey blindingKey = null;
  73:   private RSAPublicKey encipherKey = null;
  74:   private SecureRandom random = null;
  75:   private byte[] dataBuffer = null;
  76:   private int pos = 0;
  77: 
  78:   protected void engineSetMode(String mode) throws NoSuchAlgorithmException
  79:   {
  80:     throw new NoSuchAlgorithmException("only one mode available");
  81:   }
  82: 
  83:   protected void engineSetPadding(String pad) throws NoSuchPaddingException
  84:   {
  85:     throw new NoSuchPaddingException("only one padding available");
  86:   }
  87: 
  88:   protected int engineGetBlockSize()
  89:   {
  90:     return 1;
  91:   }
  92: 
  93:   protected int engineGetOutputSize(int inputLen)
  94:   {
  95:     int outputLen = 0;
  96:     if (decipherKey != null)
  97:       outputLen = (decipherKey.getModulus().bitLength() + 7) / 8;
  98:     else if (encipherKey != null)
  99:       outputLen = (encipherKey.getModulus().bitLength() + 7) / 8;
 100:     else
 101:       throw new IllegalStateException("not initialized");
 102:     if (inputLen > outputLen)
 103:       throw new IllegalArgumentException("not configured to encode " + inputLen
 104:                                          + "bytes; at most " + outputLen);
 105:     return outputLen;
 106:   }
 107: 
 108:   protected int engineGetKeySize(final Key key) throws InvalidKeyException
 109:   {
 110:     if (! (key instanceof RSAKey))
 111:       throw new InvalidKeyException("not an RSA key");
 112:     return ((RSAKey) key).getModulus().bitLength();
 113:   }
 114: 
 115:   protected byte[] engineGetIV()
 116:   {
 117:     return null;
 118:   }
 119: 
 120:   protected AlgorithmParameters engineGetParameters()
 121:   {
 122:     return null;
 123:   }
 124: 
 125:   protected void engineInit(int opmode, Key key, SecureRandom random)
 126:       throws InvalidKeyException
 127:   {
 128:     int outputLen = 0;
 129:     if (opmode == Cipher.ENCRYPT_MODE)
 130:       {
 131:         if (! (key instanceof RSAPublicKey))
 132:           throw new InvalidKeyException("expecting a RSAPublicKey");
 133:         encipherKey = (RSAPublicKey) key;
 134:         decipherKey = null;
 135:         blindingKey = null;
 136:         outputLen = (encipherKey.getModulus().bitLength() + 7) / 8;
 137:       }
 138:     else if (opmode == Cipher.DECRYPT_MODE)
 139:       {
 140:         if (key instanceof RSAPrivateKey)
 141:           {
 142:             decipherKey = (RSAPrivateKey) key;
 143:             encipherKey = null;
 144:             blindingKey = null;
 145:             outputLen = (decipherKey.getModulus().bitLength() + 7) / 8;
 146:           }
 147:         else if (key instanceof RSAPublicKey)
 148:           {
 149:             if (decipherKey == null)
 150:               throw new IllegalStateException("must configure decryption key first");
 151:             if (! decipherKey.getModulus().equals(((RSAPublicKey) key).getModulus()))
 152:               throw new InvalidKeyException("blinding key is not compatible");
 153:             blindingKey = (RSAPublicKey) key;
 154:             return;
 155:           }
 156:         else
 157:           throw new InvalidKeyException(
 158:               "expecting either an RSAPrivateKey or an RSAPublicKey (for blinding)");
 159:       }
 160:     else
 161:       throw new IllegalArgumentException("only encryption and decryption supported");
 162:     this.random = random;
 163:     this.opmode = opmode;
 164:     pos = 0;
 165:     dataBuffer = new byte[outputLen];
 166:   }
 167: 
 168:   protected void engineInit(int opmode, Key key, AlgorithmParameterSpec spec,
 169:                             SecureRandom random) throws InvalidKeyException
 170:   {
 171:     engineInit(opmode, key, random);
 172:   }
 173: 
 174:   protected void engineInit(int opmode, Key key, AlgorithmParameters params,
 175:                             SecureRandom random) throws InvalidKeyException
 176:   {
 177:     engineInit(opmode, key, random);
 178:   }
 179: 
 180:   protected byte[] engineUpdate(byte[] in, int offset, int length)
 181:   {
 182:     if (opmode != Cipher.ENCRYPT_MODE && opmode != Cipher.DECRYPT_MODE)
 183:       throw new IllegalStateException("not initialized");
 184:     System.arraycopy(in, offset, dataBuffer, pos, length);
 185:     pos += length;
 186:     return EMPTY;
 187:   }
 188: 
 189:   protected int engineUpdate(byte[] in, int offset, int length, byte[] out,
 190:                              int outOffset)
 191:   {
 192:     engineUpdate(in, offset, length);
 193:     return 0;
 194:   }
 195: 
 196:   protected byte[] engineDoFinal(byte[] in, int offset, int length)
 197:       throws IllegalBlockSizeException, BadPaddingException
 198:   {
 199:     engineUpdate(in, offset, length);
 200:     if (opmode == Cipher.DECRYPT_MODE)
 201:       {
 202:         if (pos < dataBuffer.length)
 203:           throw new IllegalBlockSizeException("expecting exactly "
 204:                                               + dataBuffer.length + " bytes");
 205:         BigInteger enc = new BigInteger(1, dataBuffer);
 206:         byte[] dec = rsaDecrypt(enc);
 207:         logger.log(Component.CRYPTO, "RSA: decryption produced\n{0}",
 208:                    new ByteArray(dec));
 209:         if (dec[0] != 0x02)
 210:           throw new BadPaddingException("expected padding type 2");
 211:         int i;
 212:         for (i = 1; i < dec.length && dec[i] != 0x00; i++)
 213:           ; // keep incrementing i
 214:         int len = dec.length - i - 1; // skip the 0x00 byte
 215:         byte[] result = new byte[len];
 216:         System.arraycopy(dec, i + 1, result, 0, len);
 217:         pos = 0;
 218:         return result;
 219:       }
 220:     else
 221:       {
 222:         offset = dataBuffer.length - pos;
 223:         if (offset < 3)
 224:           throw new IllegalBlockSizeException("input is too large to encrypt");
 225:         byte[] dec = new byte[dataBuffer.length];
 226:         dec[0] = 0x02;
 227:         if (random == null)
 228:           random = new SecureRandom();
 229:         byte[] pad = new byte[offset - 2];
 230:         random.nextBytes(pad);
 231:         for (int i = 0; i < pad.length; i++)
 232:           if (pad[i] == 0)
 233:             pad[i] = 1;
 234:         System.arraycopy(pad, 0, dec, 1, pad.length);
 235:         dec[dec.length - pos] = 0x00;
 236:         System.arraycopy(dataBuffer, 0, dec, offset, pos);
 237:         logger.log(Component.CRYPTO, "RSA: produced padded plaintext\n{0}",
 238:                    new ByteArray(dec));
 239:         BigInteger x = new BigInteger(1, dec);
 240:         BigInteger y = x.modPow(encipherKey.getPublicExponent(),
 241:                                 encipherKey.getModulus());
 242:         byte[] enc = y.toByteArray();
 243:         if (enc[0] == 0x00)
 244:           {
 245:             byte[] tmp = new byte[enc.length - 1];
 246:             System.arraycopy(enc, 1, tmp, 0, tmp.length);
 247:             enc = tmp;
 248:           }
 249:         pos = 0;
 250:         return enc;
 251:       }
 252:   }
 253: 
 254:   protected int engineDoFinal(byte[] out, int offset)
 255:       throws ShortBufferException, IllegalBlockSizeException,
 256:       BadPaddingException
 257:   {
 258:     byte[] result = engineDoFinal(EMPTY, 0, 0);
 259:     if (out.length - offset < result.length)
 260:       throw new ShortBufferException("need " + result.length + ", have "
 261:                                      + (out.length - offset));
 262:     System.arraycopy(result, 0, out, offset, result.length);
 263:     return result.length;
 264:   }
 265: 
 266:   protected int engineDoFinal(final byte[] input, final int offset,
 267:                               final int length, final byte[] output,
 268:                               final int outputOffset)
 269:       throws ShortBufferException, IllegalBlockSizeException,
 270:       BadPaddingException
 271:   {
 272:     byte[] result = engineDoFinal(input, offset, length);
 273:     if (output.length - outputOffset < result.length)
 274:       throw new ShortBufferException("need " + result.length + ", have "
 275:                                      + (output.length - outputOffset));
 276:     System.arraycopy(result, 0, output, outputOffset, result.length);
 277:     return result.length;
 278:   }
 279: 
 280:   /**
 281:    * Decrypts the ciphertext, employing RSA blinding if possible.
 282:    */
 283:   private byte[] rsaDecrypt(BigInteger enc)
 284:   {
 285:     if (random == null)
 286:       random = new SecureRandom();
 287:     BigInteger n = decipherKey.getModulus();
 288:     BigInteger r = null;
 289:     BigInteger pubExp = null;
 290:     if (blindingKey != null)
 291:       pubExp = blindingKey.getPublicExponent();
 292:     if (pubExp != null && (decipherKey instanceof RSAPrivateCrtKey))
 293:       pubExp = ((RSAPrivateCrtKey) decipherKey).getPublicExponent();
 294:     if (pubExp != null)
 295:       {
 296:         r = new BigInteger(n.bitLength() - 1, random);
 297:         enc = r.modPow(pubExp, n).multiply(enc).mod(n);
 298:       }
 299:     BigInteger dec = enc.modPow(decipherKey.getPrivateExponent(), n);
 300:     if (pubExp != null)
 301:       dec = dec.multiply(r.modInverse(n)).mod(n);
 302:     return dec.toByteArray();
 303:   }
 304: }