Source for gnu.javax.crypto.RSACipherImpl

   1: /* DiffieHellmanImpl.java -- implementation of the Diffie-Hellman key agreement.
   2:    Copyright (C) 2005  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.ByteArray;
  42: import gnu.classpath.debug.Component;
  43: import gnu.classpath.debug.SystemLogger;
  44: 
  45: import java.math.BigInteger;
  46: 
  47: import java.security.AlgorithmParameters;
  48: import java.security.InvalidAlgorithmParameterException;
  49: import java.security.InvalidKeyException;
  50: import java.security.Key;
  51: import java.security.NoSuchAlgorithmException;
  52: import java.security.SecureRandom;
  53: 
  54: import java.security.interfaces.RSAKey;
  55: import java.security.interfaces.RSAPrivateKey;
  56: import java.security.interfaces.RSAPrivateCrtKey;
  57: import java.security.interfaces.RSAPublicKey;
  58: 
  59: import java.security.spec.AlgorithmParameterSpec;
  60: 
  61: import java.util.logging.Logger;
  62: 
  63: import javax.crypto.BadPaddingException;
  64: import javax.crypto.Cipher;
  65: import javax.crypto.CipherSpi;
  66: import javax.crypto.IllegalBlockSizeException;
  67: import javax.crypto.NoSuchPaddingException;
  68: import javax.crypto.ShortBufferException;
  69: 
  70: public class RSACipherImpl extends CipherSpi
  71: {
  72:   private static final Logger logger = SystemLogger.SYSTEM;
  73: 
  74:   private static final byte[] EMPTY = new byte[0];
  75:   private int opmode = -1;
  76:   private RSAPrivateKey decipherKey = null;
  77:   private RSAPublicKey blindingKey = null;
  78:   private RSAPublicKey encipherKey = null;
  79:   private SecureRandom random = null;
  80:   private byte[] dataBuffer = null;
  81:   private int pos = 0;
  82: 
  83:   protected void engineSetMode (String mode) throws NoSuchAlgorithmException
  84:   {
  85:     throw new NoSuchAlgorithmException ("only one mode available");
  86:   }
  87: 
  88:   protected void engineSetPadding (String pad) throws NoSuchPaddingException
  89:   {
  90:     throw new NoSuchPaddingException ("only one padding available");
  91:   }
  92: 
  93:   protected int engineGetBlockSize ()
  94:   {
  95:     return 1;
  96:   }
  97: 
  98:   protected int engineGetOutputSize (int inputLen)
  99:   {
 100:     int outputLen = 0;
 101:     if (decipherKey != null)
 102:       {
 103:         outputLen = (decipherKey.getModulus ().bitLength () + 7) / 8;
 104:       }
 105:     else if (encipherKey != null)
 106:       {
 107:         outputLen = (encipherKey.getModulus ().bitLength () + 7) / 8;
 108:       }
 109:     else
 110:       throw new IllegalStateException ("not initialized");
 111:     if (inputLen > outputLen)
 112:       throw new IllegalArgumentException ("not configured to encode " + inputLen
 113:                                           + "bytes; at most " + outputLen);
 114:     return outputLen;
 115:   }
 116: 
 117:   protected int engineGetKeySize (final Key key) throws InvalidKeyException
 118:   {
 119:     if (!(key instanceof RSAKey))
 120:       throw new InvalidKeyException ("not an RSA key");
 121:     return ((RSAKey) key).getModulus ().bitLength ();
 122:   }
 123: 
 124:   protected byte[] engineGetIV ()
 125:   {
 126:     return null;
 127:   }
 128: 
 129:   protected AlgorithmParameters engineGetParameters()
 130:   {
 131:     return null;
 132:   }
 133: 
 134:   protected void engineInit (int opmode, Key key, SecureRandom random)
 135:     throws InvalidKeyException
 136:   {
 137:     int outputLen = 0;
 138:     if (opmode == Cipher.ENCRYPT_MODE)
 139:       {
 140:         if (!(key instanceof RSAPublicKey))
 141:           throw new InvalidKeyException ("expecting a RSAPublicKey");
 142:         encipherKey = (RSAPublicKey) key;
 143:         decipherKey = null;
 144:         blindingKey = null;
 145:         outputLen = (encipherKey.getModulus ().bitLength () + 7) / 8;
 146:       }
 147:     else if (opmode == Cipher.DECRYPT_MODE)
 148:       {
 149:         if (key instanceof RSAPrivateKey)
 150:           {
 151:             decipherKey = (RSAPrivateKey) key;
 152:             encipherKey = null;
 153:             blindingKey = null;
 154:             outputLen = (decipherKey.getModulus ().bitLength () + 7) / 8;
 155:           }
 156:         else if (key instanceof RSAPublicKey)
 157:           {
 158:             if (decipherKey == null)
 159:               throw new IllegalStateException ("must configure decryption key first");
 160:             if (!decipherKey.getModulus ().equals (((RSAPublicKey) key).getModulus ()))
 161:               throw new InvalidKeyException ("blinding key is not compatible");
 162:             blindingKey = (RSAPublicKey) key;
 163:             return;
 164:           }
 165:         else
 166:           throw new InvalidKeyException ("expecting either an RSAPrivateKey or an RSAPublicKey (for blinding)");
 167:       }
 168:     else
 169:       throw new IllegalArgumentException ("only encryption and decryption supported");
 170:     this.random = random;
 171:     this.opmode = opmode;
 172:     pos = 0;
 173:     dataBuffer = new byte[outputLen];
 174:   }
 175: 
 176:   protected void engineInit (int opmode, Key key, AlgorithmParameterSpec spec, SecureRandom random)
 177:     throws InvalidKeyException
 178:   {
 179:     engineInit (opmode, key, random);
 180:   }
 181: 
 182:   protected void engineInit (int opmode, Key key, AlgorithmParameters params, SecureRandom random)
 183:     throws InvalidKeyException
 184:   {
 185:     engineInit (opmode, key, random);
 186:   }
 187: 
 188:   protected byte[] engineUpdate (byte[] in, int offset, int length)
 189:   {
 190:     if (opmode != Cipher.ENCRYPT_MODE && opmode != Cipher.DECRYPT_MODE)
 191:       throw new IllegalStateException ("not initialized");
 192:     System.arraycopy (in, offset, dataBuffer, pos, length);
 193:     pos += length;
 194:     return EMPTY;
 195:   }
 196: 
 197:   protected int engineUpdate (byte[] in, int offset, int length, byte[] out, int outOffset)
 198:   {
 199:     engineUpdate (in, offset, length);
 200:     return 0;
 201:   }
 202: 
 203:   protected byte[] engineDoFinal (byte[] in, int offset, int length)
 204:     throws IllegalBlockSizeException, BadPaddingException
 205:   {
 206:     engineUpdate (in, offset, length);
 207:     if (opmode == Cipher.DECRYPT_MODE)
 208:       {
 209:         if (pos < dataBuffer.length)
 210:           throw new IllegalBlockSizeException ("expecting exactly " + dataBuffer.length + " bytes");
 211:         BigInteger enc = new BigInteger (1, dataBuffer);
 212:         byte[] dec = rsaDecrypt (enc);
 213:     logger.log (Component.CRYPTO, "RSA: decryption produced\n{0}",
 214:             new ByteArray (dec));
 215:         if (dec[0] != 0x02)
 216:           throw new BadPaddingException ("expected padding type 2");
 217:         int i;
 218:         for (i = 1; i < dec.length && dec[i] != 0x00; i++);
 219:         int len = dec.length - i;
 220:         byte[] result = new byte[len];
 221:         System.arraycopy (dec, i, result, 0, len);
 222:         pos = 0;
 223:         return result;
 224:       }
 225:     else
 226:       {
 227:         offset = dataBuffer.length - pos;
 228:         if (offset < 3)
 229:           throw new IllegalBlockSizeException ("input is too large to encrypt");
 230:         byte[] dec = new byte[dataBuffer.length];
 231:         dec[0] = 0x02;
 232:         if (random == null)
 233:           random = new SecureRandom ();
 234:     byte[] pad = new byte[offset - 2];
 235:     random.nextBytes (pad);
 236:     for (int i = 0; i < pad.length; i++)
 237:       if (pad[i] == 0)
 238:         pad[i] = 1;
 239:     System.arraycopy (pad, 0, dec, 1, pad.length);
 240:         dec[dec.length - pos] = 0x00;
 241:         System.arraycopy (dataBuffer, 0, dec, offset, pos);
 242:     logger.log (Component.CRYPTO, "RSA: produced padded plaintext\n{0}",
 243:             new ByteArray (dec));
 244:         BigInteger x = new BigInteger (1, dec);
 245:         BigInteger y = x.modPow (encipherKey.getPublicExponent (),
 246:                                  encipherKey.getModulus ());
 247:         byte[] enc = y.toByteArray ();
 248:         if (enc[0] == 0x00)
 249:           {
 250:             byte[] tmp = new byte[enc.length - 1];
 251:             System.arraycopy (enc, 1, tmp, 0, tmp.length);
 252:             enc = tmp;
 253:           }
 254:         pos = 0;
 255:         return enc;
 256:       }
 257:   }
 258: 
 259:   protected int engineDoFinal (byte[] out, int offset)
 260:     throws ShortBufferException, IllegalBlockSizeException, BadPaddingException
 261:   {
 262:     byte[] result = engineDoFinal (EMPTY, 0, 0);
 263:     if (out.length - offset < result.length)
 264:       throw new ShortBufferException ("need " + result.length + ", have "
 265:                                       + (out.length - offset));
 266:     System.arraycopy (result, 0, out, offset, result.length);
 267:     return result.length;
 268:   }
 269: 
 270:   protected int engineDoFinal (final byte[] input, final int offset, final int length,
 271:                    final byte[] output, final int outputOffset)
 272:     throws ShortBufferException, IllegalBlockSizeException, BadPaddingException
 273:   {
 274:     byte[] result = engineDoFinal (input, offset, length);
 275:     if (output.length - outputOffset < result.length)
 276:       throw new ShortBufferException ("need " + result.length + ", have "
 277:                       + (output.length - outputOffset));
 278:     System.arraycopy (result, 0, output, outputOffset, result.length);
 279:     return result.length;
 280:   }
 281: 
 282:   /**
 283:    * Decrypts the ciphertext, employing RSA blinding if possible.
 284:    */
 285:   private byte[] rsaDecrypt (BigInteger enc)
 286:   {
 287:     if (random == null)
 288:       random = new SecureRandom ();
 289:     BigInteger n = decipherKey.getModulus ();
 290:     BigInteger r = null;
 291:     BigInteger pubExp = null;
 292:     if (blindingKey != null)
 293:       pubExp = blindingKey.getPublicExponent ();
 294:     if (pubExp != null && (decipherKey instanceof RSAPrivateCrtKey))
 295:       pubExp = ((RSAPrivateCrtKey) decipherKey).getPublicExponent ();
 296:     if (pubExp != null)
 297:       {
 298:         r = new BigInteger (n.bitLength () - 1, random);
 299:         enc = r.modPow (pubExp, n).multiply (enc).mod (n);
 300:       }
 301: 
 302:     BigInteger dec = enc.modPow (decipherKey.getPrivateExponent (), n);
 303: 
 304:     if (pubExp != null)
 305:       {
 306:         dec = dec.multiply (r.modInverse (n)).mod (n);
 307:       }
 308: 
 309:     return dec.toByteArray ();
 310:   }
 311: }