Save This Page
Home » openjdk-7 » com.sun.crypto » provider » [javadoc | source]
    1   /*
    2    * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package com.sun.crypto.provider;
   27   
   28   import java.util.Locale;
   29   
   30   import java.security;
   31   import java.security.interfaces;
   32   import java.security.spec.AlgorithmParameterSpec;
   33   import java.security.spec.InvalidParameterSpecException;
   34   import java.security.spec.MGF1ParameterSpec;
   35   
   36   import javax.crypto;
   37   import javax.crypto.spec.PSource;
   38   import javax.crypto.spec.OAEPParameterSpec;
   39   
   40   import sun.security.rsa;
   41   import sun.security.jca.Providers;
   42   
   43   /**
   44    * RSA cipher implementation. Supports RSA en/decryption and signing/verifying
   45    * using PKCS#1 v1.5 padding and without padding (raw RSA). Note that raw RSA
   46    * is supported mostly for completeness and should only be used in rare cases.
   47    *
   48    * Objects should be instantiated by calling Cipher.getInstance() using the
   49    * following algorithm names:
   50    *  . "RSA/ECB/PKCS1Padding" (or "RSA") for PKCS#1 padding. The mode (blocktype)
   51    *    is selected based on the en/decryption mode and public/private key used
   52    *  . "RSA/ECB/NoPadding" for rsa RSA.
   53    *
   54    * We only do one RSA operation per doFinal() call. If the application passes
   55    * more data via calls to update() or doFinal(), we throw an
   56    * IllegalBlockSizeException when doFinal() is called (see JCE API spec).
   57    * Bulk encryption using RSA does not make sense and is not standardized.
   58    *
   59    * Note: RSA keys should be at least 512 bits long
   60    *
   61    * @since   1.5
   62    * @author  Andreas Sterbenz
   63    */
   64   public final class RSACipher extends CipherSpi {
   65   
   66       // constant for an empty byte array
   67       private final static byte[] B0 = new byte[0];
   68   
   69       // mode constant for public key encryption
   70       private final static int MODE_ENCRYPT = 1;
   71       // mode constant for private key decryption
   72       private final static int MODE_DECRYPT = 2;
   73       // mode constant for private key encryption (signing)
   74       private final static int MODE_SIGN    = 3;
   75       // mode constant for public key decryption (verifying)
   76       private final static int MODE_VERIFY  = 4;
   77   
   78       // constant for raw RSA
   79       private final static String PAD_NONE  = "NoPadding";
   80       // constant for PKCS#1 v1.5 RSA
   81       private final static String PAD_PKCS1 = "PKCS1Padding";
   82       // constant for PKCS#2 v2.0 OAEP with MGF1
   83       private final static String PAD_OAEP_MGF1  = "OAEP";
   84   
   85       // current mode, one of MODE_* above. Set when init() is called
   86       private int mode;
   87   
   88       // active padding type, one of PAD_* above. Set by setPadding()
   89       private String paddingType;
   90   
   91       // padding object
   92       private RSAPadding padding;
   93   
   94       // cipher parameter for OAEP padding
   95       private OAEPParameterSpec spec = null;
   96   
   97       // buffer for the data
   98       private byte[] buffer;
   99       // offset into the buffer (number of bytes buffered)
  100       private int bufOfs;
  101   
  102       // size of the output
  103       private int outputSize;
  104   
  105       // the public key, if we were initialized using a public key
  106       private RSAPublicKey publicKey;
  107       // the private key, if we were initialized using a private key
  108       private RSAPrivateKey privateKey;
  109   
  110       // hash algorithm for OAEP
  111       private String oaepHashAlgorithm = "SHA-1";
  112   
  113       public RSACipher() {
  114           paddingType = PAD_PKCS1;
  115       }
  116   
  117       // modes do not make sense for RSA, but allow ECB
  118       // see JCE spec
  119       protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
  120           if (mode.equalsIgnoreCase("ECB") == false) {
  121               throw new NoSuchAlgorithmException("Unsupported mode " + mode);
  122           }
  123       }
  124   
  125       // set the padding type
  126       // see JCE spec
  127       protected void engineSetPadding(String paddingName)
  128               throws NoSuchPaddingException {
  129           if (paddingName.equalsIgnoreCase(PAD_NONE)) {
  130               paddingType = PAD_NONE;
  131           } else if (paddingName.equalsIgnoreCase(PAD_PKCS1)) {
  132               paddingType = PAD_PKCS1;
  133           } else {
  134               String lowerPadding = paddingName.toLowerCase(Locale.ENGLISH);
  135               if (lowerPadding.equals("oaeppadding")) {
  136                   paddingType = PAD_OAEP_MGF1;
  137               } else if (lowerPadding.startsWith("oaepwith") &&
  138                          lowerPadding.endsWith("andmgf1padding")) {
  139                   paddingType = PAD_OAEP_MGF1;
  140                   // "oaepwith".length() == 8
  141                   // "andmgf1padding".length() == 14
  142                   oaepHashAlgorithm =
  143                           paddingName.substring(8, paddingName.length() - 14);
  144                   // check if MessageDigest appears to be available
  145                   // avoid getInstance() call here
  146                   if (Providers.getProviderList().getService
  147                           ("MessageDigest", oaepHashAlgorithm) == null) {
  148                       throw new NoSuchPaddingException
  149                           ("MessageDigest not available for " + paddingName);
  150                   }
  151               } else {
  152                   throw new NoSuchPaddingException
  153                       ("Padding " + paddingName + " not supported");
  154               }
  155           }
  156       }
  157   
  158       // return 0 as block size, we are not a block cipher
  159       // see JCE spec
  160       protected int engineGetBlockSize() {
  161           return 0;
  162       }
  163   
  164       // return the output size
  165       // see JCE spec
  166       protected int engineGetOutputSize(int inputLen) {
  167           return outputSize;
  168       }
  169   
  170       // no iv, return null
  171       // see JCE spec
  172       protected byte[] engineGetIV() {
  173           return null;
  174       }
  175   
  176       // see JCE spec
  177       protected AlgorithmParameters engineGetParameters() {
  178           if (spec != null) {
  179               try {
  180                   AlgorithmParameters params =
  181                       AlgorithmParameters.getInstance("OAEP", "SunJCE");
  182                   params.init(spec);
  183                   return params;
  184               } catch (NoSuchAlgorithmException nsae) {
  185                   // should never happen
  186                   throw new RuntimeException("Cannot find OAEP " +
  187                       " AlgorithmParameters implementation in SunJCE provider");
  188               } catch (NoSuchProviderException nspe) {
  189                   // should never happen
  190                   throw new RuntimeException("Cannot find SunJCE provider");
  191               } catch (InvalidParameterSpecException ipse) {
  192                   // should never happen
  193                   throw new RuntimeException("OAEPParameterSpec not supported");
  194               }
  195           } else {
  196               return null;
  197           }
  198       }
  199   
  200       // see JCE spec
  201       protected void engineInit(int opmode, Key key, SecureRandom random)
  202               throws InvalidKeyException {
  203           try {
  204               init(opmode, key, random, null);
  205           } catch (InvalidAlgorithmParameterException iape) {
  206               // never thrown when null parameters are used;
  207               // but re-throw it just in case
  208               InvalidKeyException ike =
  209                   new InvalidKeyException("Wrong parameters");
  210               ike.initCause(iape);
  211               throw ike;
  212           }
  213       }
  214   
  215       // see JCE spec
  216       protected void engineInit(int opmode, Key key,
  217               AlgorithmParameterSpec params, SecureRandom random)
  218               throws InvalidKeyException, InvalidAlgorithmParameterException {
  219           init(opmode, key, random, params);
  220       }
  221   
  222       // see JCE spec
  223       protected void engineInit(int opmode, Key key,
  224               AlgorithmParameters params, SecureRandom random)
  225               throws InvalidKeyException, InvalidAlgorithmParameterException {
  226           if (params == null) {
  227               init(opmode, key, random, null);
  228           } else {
  229               try {
  230                   OAEPParameterSpec spec = (OAEPParameterSpec)
  231                       params.getParameterSpec(OAEPParameterSpec.class);
  232                   init(opmode, key, random, spec);
  233               } catch (InvalidParameterSpecException ipse) {
  234                   InvalidAlgorithmParameterException iape =
  235                       new InvalidAlgorithmParameterException("Wrong parameter");
  236                   iape.initCause(ipse);
  237                   throw iape;
  238               }
  239           }
  240       }
  241   
  242       // initialize this cipher
  243       private void init(int opmode, Key key, SecureRandom random,
  244               AlgorithmParameterSpec params)
  245               throws InvalidKeyException, InvalidAlgorithmParameterException {
  246           boolean encrypt;
  247           switch (opmode) {
  248           case Cipher.ENCRYPT_MODE:
  249           case Cipher.WRAP_MODE:
  250               encrypt = true;
  251               break;
  252           case Cipher.DECRYPT_MODE:
  253           case Cipher.UNWRAP_MODE:
  254               encrypt = false;
  255               break;
  256           default:
  257               throw new InvalidKeyException("Unknown mode: " + opmode);
  258           }
  259           RSAKey rsaKey = RSAKeyFactory.toRSAKey(key);
  260           if (key instanceof RSAPublicKey) {
  261               mode = encrypt ? MODE_ENCRYPT : MODE_VERIFY;
  262               publicKey = (RSAPublicKey)key;
  263               privateKey = null;
  264           } else { // must be RSAPrivateKey per check in toRSAKey
  265               mode = encrypt ? MODE_SIGN : MODE_DECRYPT;
  266               privateKey = (RSAPrivateKey)key;
  267               publicKey = null;
  268           }
  269           int n = RSACore.getByteLength(rsaKey.getModulus());
  270           outputSize = n;
  271           bufOfs = 0;
  272           if (paddingType == PAD_NONE) {
  273               if (params != null) {
  274                   throw new InvalidAlgorithmParameterException
  275                   ("Parameters not supported");
  276               }
  277               padding = RSAPadding.getInstance(RSAPadding.PAD_NONE, n, random);
  278               buffer = new byte[n];
  279           } else if (paddingType == PAD_PKCS1) {
  280               if (params != null) {
  281                   throw new InvalidAlgorithmParameterException
  282                   ("Parameters not supported");
  283               }
  284               int blockType = (mode <= MODE_DECRYPT) ? RSAPadding.PAD_BLOCKTYPE_2
  285                                                      : RSAPadding.PAD_BLOCKTYPE_1;
  286               padding = RSAPadding.getInstance(blockType, n, random);
  287               if (encrypt) {
  288                   int k = padding.getMaxDataSize();
  289                   buffer = new byte[k];
  290               } else {
  291                   buffer = new byte[n];
  292               }
  293           } else { // PAD_OAEP_MGF1
  294               if ((mode == MODE_SIGN) || (mode == MODE_VERIFY)) {
  295                   throw new InvalidKeyException
  296                           ("OAEP cannot be used to sign or verify signatures");
  297               }
  298               OAEPParameterSpec myParams;
  299               if (params != null) {
  300                   if (!(params instanceof OAEPParameterSpec)) {
  301                       throw new InvalidAlgorithmParameterException
  302                           ("Wrong Parameters for OAEP Padding");
  303                   }
  304                   myParams = (OAEPParameterSpec) params;
  305               } else {
  306                   myParams = new OAEPParameterSpec(oaepHashAlgorithm, "MGF1",
  307                       MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
  308               }
  309               padding = RSAPadding.getInstance(RSAPadding.PAD_OAEP_MGF1, n,
  310                   random, myParams);
  311               if (encrypt) {
  312                   int k = padding.getMaxDataSize();
  313                   buffer = new byte[k];
  314               } else {
  315                   buffer = new byte[n];
  316               }
  317           }
  318       }
  319   
  320       // internal update method
  321       private void update(byte[] in, int inOfs, int inLen) {
  322           if ((inLen == 0) || (in == null)) {
  323               return;
  324           }
  325           if (bufOfs + inLen > buffer.length) {
  326               bufOfs = buffer.length + 1;
  327               return;
  328           }
  329           System.arraycopy(in, inOfs, buffer, bufOfs, inLen);
  330           bufOfs += inLen;
  331       }
  332   
  333       // internal doFinal() method. Here we perform the actual RSA operation
  334       private byte[] doFinal() throws BadPaddingException,
  335               IllegalBlockSizeException {
  336           if (bufOfs > buffer.length) {
  337               throw new IllegalBlockSizeException("Data must not be longer "
  338                   + "than " + buffer.length + " bytes");
  339           }
  340           try {
  341               byte[] data;
  342               switch (mode) {
  343               case MODE_SIGN:
  344                   data = padding.pad(buffer, 0, bufOfs);
  345                   return RSACore.rsa(data, privateKey);
  346               case MODE_VERIFY:
  347                   byte[] verifyBuffer = RSACore.convert(buffer, 0, bufOfs);
  348                   data = RSACore.rsa(verifyBuffer, publicKey);
  349                   return padding.unpad(data);
  350               case MODE_ENCRYPT:
  351                   data = padding.pad(buffer, 0, bufOfs);
  352                   return RSACore.rsa(data, publicKey);
  353               case MODE_DECRYPT:
  354                   byte[] decryptBuffer = RSACore.convert(buffer, 0, bufOfs);
  355                   data = RSACore.rsa(decryptBuffer, privateKey);
  356                   return padding.unpad(data);
  357               default:
  358                   throw new AssertionError("Internal error");
  359               }
  360           } finally {
  361               bufOfs = 0;
  362           }
  363       }
  364   
  365       // see JCE spec
  366       protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
  367           update(in, inOfs, inLen);
  368           return B0;
  369       }
  370   
  371       // see JCE spec
  372       protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out,
  373               int outOfs) {
  374           update(in, inOfs, inLen);
  375           return 0;
  376       }
  377   
  378       // see JCE spec
  379       protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
  380               throws BadPaddingException, IllegalBlockSizeException {
  381           update(in, inOfs, inLen);
  382           return doFinal();
  383       }
  384   
  385       // see JCE spec
  386       protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
  387               int outOfs) throws ShortBufferException, BadPaddingException,
  388               IllegalBlockSizeException {
  389           if (outputSize > out.length - outOfs) {
  390               throw new ShortBufferException
  391                   ("Need " + outputSize + " bytes for output");
  392           }
  393           update(in, inOfs, inLen);
  394           byte[] result = doFinal();
  395           int n = result.length;
  396           System.arraycopy(result, 0, out, outOfs, n);
  397           return n;
  398       }
  399   
  400       // see JCE spec
  401       protected byte[] engineWrap(Key key) throws InvalidKeyException,
  402               IllegalBlockSizeException {
  403           byte[] encoded = key.getEncoded();
  404           if ((encoded == null) || (encoded.length == 0)) {
  405               throw new InvalidKeyException("Could not obtain encoded key");
  406           }
  407           if (encoded.length > buffer.length) {
  408               throw new InvalidKeyException("Key is too long for wrapping");
  409           }
  410           update(encoded, 0, encoded.length);
  411           try {
  412               return doFinal();
  413           } catch (BadPaddingException e) {
  414               // should not occur
  415               throw new InvalidKeyException("Wrapping failed", e);
  416           }
  417       }
  418   
  419       // see JCE spec
  420       protected Key engineUnwrap(byte[] wrappedKey, String algorithm,
  421               int type) throws InvalidKeyException, NoSuchAlgorithmException {
  422           if (wrappedKey.length > buffer.length) {
  423               throw new InvalidKeyException("Key is too long for unwrapping");
  424           }
  425           update(wrappedKey, 0, wrappedKey.length);
  426           try {
  427               byte[] encoded = doFinal();
  428               return ConstructKeys.constructKey(encoded, algorithm, type);
  429           } catch (BadPaddingException e) {
  430               // should not occur
  431               throw new InvalidKeyException("Unwrapping failed", e);
  432           } catch (IllegalBlockSizeException e) {
  433               // should not occur, handled with length check above
  434               throw new InvalidKeyException("Unwrapping failed", e);
  435           }
  436       }
  437   
  438       // see JCE spec
  439       protected int engineGetKeySize(Key key) throws InvalidKeyException {
  440           RSAKey rsaKey = RSAKeyFactory.toRSAKey(key);
  441           return rsaKey.getModulus().bitLength();
  442       }
  443   
  444   }

Save This Page
Home » openjdk-7 » com.sun.crypto » provider » [javadoc | source]