blob: e5c6c019e0f64057d38554e4e990d67d4040e75a [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.crypto.provider;
27
28import java.util.Locale;
29
30import java.security.*;
31import java.security.interfaces.*;
32import java.security.spec.AlgorithmParameterSpec;
33import java.security.spec.InvalidParameterSpecException;
34import java.security.spec.MGF1ParameterSpec;
35
36import javax.crypto.*;
37import javax.crypto.spec.PSource;
38import javax.crypto.spec.OAEPParameterSpec;
39
40import sun.security.rsa.*;
41import 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 */
64public 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 SunJCE.ensureIntegrity(getClass());
115 paddingType = PAD_PKCS1;
116 }
117
118 // modes do not make sense for RSA, but allow ECB
119 // see JCE spec
120 protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
121 if (mode.equalsIgnoreCase("ECB") == false) {
122 throw new NoSuchAlgorithmException("Unsupported mode " + mode);
123 }
124 }
125
126 // set the padding type
127 // see JCE spec
128 protected void engineSetPadding(String paddingName)
129 throws NoSuchPaddingException {
130 if (paddingName.equalsIgnoreCase(PAD_NONE)) {
131 paddingType = PAD_NONE;
132 } else if (paddingName.equalsIgnoreCase(PAD_PKCS1)) {
133 paddingType = PAD_PKCS1;
134 } else {
135 String lowerPadding = paddingName.toLowerCase(Locale.ENGLISH);
136 if (lowerPadding.equals("oaeppadding")) {
137 paddingType = PAD_OAEP_MGF1;
138 } else if (lowerPadding.startsWith("oaepwith") &&
139 lowerPadding.endsWith("andmgf1padding")) {
140 paddingType = PAD_OAEP_MGF1;
141 // "oaepwith".length() == 8
142 // "andmgf1padding".length() == 14
143 oaepHashAlgorithm =
144 paddingName.substring(8, paddingName.length() - 14);
145 // check if MessageDigest appears to be available
146 // avoid getInstance() call here
147 if (Providers.getProviderList().getService
148 ("MessageDigest", oaepHashAlgorithm) == null) {
149 throw new NoSuchPaddingException
150 ("MessageDigest not available for " + paddingName);
151 }
152 } else {
153 throw new NoSuchPaddingException
154 ("Padding " + paddingName + " not supported");
155 }
156 }
157 }
158
159 // return 0 as block size, we are not a block cipher
160 // see JCE spec
161 protected int engineGetBlockSize() {
162 return 0;
163 }
164
165 // return the output size
166 // see JCE spec
167 protected int engineGetOutputSize(int inputLen) {
168 return outputSize;
169 }
170
171 // no iv, return null
172 // see JCE spec
173 protected byte[] engineGetIV() {
174 return null;
175 }
176
177 // see JCE spec
178 protected AlgorithmParameters engineGetParameters() {
179 if (spec != null) {
180 try {
181 AlgorithmParameters params =
182 AlgorithmParameters.getInstance("OAEP", "SunJCE");
183 params.init(spec);
184 return params;
185 } catch (NoSuchAlgorithmException nsae) {
186 // should never happen
187 throw new RuntimeException("Cannot find OAEP " +
188 " AlgorithmParameters implementation in SunJCE provider");
189 } catch (NoSuchProviderException nspe) {
190 // should never happen
191 throw new RuntimeException("Cannot find SunJCE provider");
192 } catch (InvalidParameterSpecException ipse) {
193 // should never happen
194 throw new RuntimeException("OAEPParameterSpec not supported");
195 }
196 } else {
197 return null;
198 }
199 }
200
201 // see JCE spec
202 protected void engineInit(int opmode, Key key, SecureRandom random)
203 throws InvalidKeyException {
204 try {
205 init(opmode, key, random, null);
206 } catch (InvalidAlgorithmParameterException iape) {
207 // never thrown when null parameters are used;
208 // but re-throw it just in case
209 InvalidKeyException ike =
210 new InvalidKeyException("Wrong parameters");
211 ike.initCause(iape);
212 throw ike;
213 }
214 }
215
216 // see JCE spec
217 protected void engineInit(int opmode, Key key,
218 AlgorithmParameterSpec params, SecureRandom random)
219 throws InvalidKeyException, InvalidAlgorithmParameterException {
220 init(opmode, key, random, params);
221 }
222
223 // see JCE spec
224 protected void engineInit(int opmode, Key key,
225 AlgorithmParameters params, SecureRandom random)
226 throws InvalidKeyException, InvalidAlgorithmParameterException {
227 if (params == null) {
228 init(opmode, key, random, null);
229 } else {
230 try {
231 OAEPParameterSpec spec = (OAEPParameterSpec)
232 params.getParameterSpec(OAEPParameterSpec.class);
233 init(opmode, key, random, spec);
234 } catch (InvalidParameterSpecException ipse) {
235 InvalidAlgorithmParameterException iape =
236 new InvalidAlgorithmParameterException("Wrong parameter");
237 iape.initCause(ipse);
238 throw iape;
239 }
240 }
241 }
242
243 // initialize this cipher
244 private void init(int opmode, Key key, SecureRandom random,
245 AlgorithmParameterSpec params)
246 throws InvalidKeyException, InvalidAlgorithmParameterException {
247 boolean encrypt;
248 switch (opmode) {
249 case Cipher.ENCRYPT_MODE:
250 case Cipher.WRAP_MODE:
251 encrypt = true;
252 break;
253 case Cipher.DECRYPT_MODE:
254 case Cipher.UNWRAP_MODE:
255 encrypt = false;
256 break;
257 default:
258 throw new InvalidKeyException("Unknown mode: " + opmode);
259 }
260 RSAKey rsaKey = RSAKeyFactory.toRSAKey(key);
261 if (key instanceof RSAPublicKey) {
262 mode = encrypt ? MODE_ENCRYPT : MODE_VERIFY;
263 publicKey = (RSAPublicKey)key;
264 privateKey = null;
265 } else { // must be RSAPrivateKey per check in toRSAKey
266 mode = encrypt ? MODE_SIGN : MODE_DECRYPT;
267 privateKey = (RSAPrivateKey)key;
268 publicKey = null;
269 }
270 int n = RSACore.getByteLength(rsaKey.getModulus());
271 outputSize = n;
272 bufOfs = 0;
273 if (paddingType == PAD_NONE) {
274 if (params != null) {
275 throw new InvalidAlgorithmParameterException
276 ("Parameters not supported");
277 }
278 padding = RSAPadding.getInstance(RSAPadding.PAD_NONE, n, random);
279 buffer = new byte[n];
280 } else if (paddingType == PAD_PKCS1) {
281 if (params != null) {
282 throw new InvalidAlgorithmParameterException
283 ("Parameters not supported");
284 }
285 int blockType = (mode <= MODE_DECRYPT) ? RSAPadding.PAD_BLOCKTYPE_2
286 : RSAPadding.PAD_BLOCKTYPE_1;
287 padding = RSAPadding.getInstance(blockType, n, random);
288 if (encrypt) {
289 int k = padding.getMaxDataSize();
290 buffer = new byte[k];
291 } else {
292 buffer = new byte[n];
293 }
294 } else { // PAD_OAEP_MGF1
295 if ((mode == MODE_SIGN) || (mode == MODE_VERIFY)) {
296 throw new InvalidKeyException
297 ("OAEP cannot be used to sign or verify signatures");
298 }
299 OAEPParameterSpec myParams;
300 if (params != null) {
301 if (!(params instanceof OAEPParameterSpec)) {
302 throw new InvalidAlgorithmParameterException
303 ("Wrong Parameters for OAEP Padding");
304 }
305 myParams = (OAEPParameterSpec) params;
306 } else {
307 myParams = new OAEPParameterSpec(oaepHashAlgorithm, "MGF1",
308 MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
309 }
310 padding = RSAPadding.getInstance(RSAPadding.PAD_OAEP_MGF1, n,
311 random, myParams);
312 if (encrypt) {
313 int k = padding.getMaxDataSize();
314 buffer = new byte[k];
315 } else {
316 buffer = new byte[n];
317 }
318 }
319 }
320
321 // internal update method
322 private void update(byte[] in, int inOfs, int inLen) {
323 if ((inLen == 0) || (in == null)) {
324 return;
325 }
326 if (bufOfs + inLen > buffer.length) {
327 bufOfs = buffer.length + 1;
328 return;
329 }
330 System.arraycopy(in, inOfs, buffer, bufOfs, inLen);
331 bufOfs += inLen;
332 }
333
334 // internal doFinal() method. Here we perform the actual RSA operation
335 private byte[] doFinal() throws BadPaddingException,
336 IllegalBlockSizeException {
337 if (bufOfs > buffer.length) {
338 throw new IllegalBlockSizeException("Data must not be longer "
339 + "than " + buffer.length + " bytes");
340 }
341 try {
342 byte[] data;
343 switch (mode) {
344 case MODE_SIGN:
345 data = padding.pad(buffer, 0, bufOfs);
346 return RSACore.rsa(data, privateKey);
347 case MODE_VERIFY:
348 byte[] verifyBuffer = RSACore.convert(buffer, 0, bufOfs);
349 data = RSACore.rsa(verifyBuffer, publicKey);
350 return padding.unpad(data);
351 case MODE_ENCRYPT:
352 data = padding.pad(buffer, 0, bufOfs);
353 return RSACore.rsa(data, publicKey);
354 case MODE_DECRYPT:
355 byte[] decryptBuffer = RSACore.convert(buffer, 0, bufOfs);
356 data = RSACore.rsa(decryptBuffer, privateKey);
357 return padding.unpad(data);
358 default:
359 throw new AssertionError("Internal error");
360 }
361 } finally {
362 bufOfs = 0;
363 }
364 }
365
366 // see JCE spec
367 protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
368 update(in, inOfs, inLen);
369 return B0;
370 }
371
372 // see JCE spec
373 protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out,
374 int outOfs) {
375 update(in, inOfs, inLen);
376 return 0;
377 }
378
379 // see JCE spec
380 protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
381 throws BadPaddingException, IllegalBlockSizeException {
382 update(in, inOfs, inLen);
383 return doFinal();
384 }
385
386 // see JCE spec
387 protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
388 int outOfs) throws ShortBufferException, BadPaddingException,
389 IllegalBlockSizeException {
390 if (outputSize > out.length - outOfs) {
391 throw new ShortBufferException
392 ("Need " + outputSize + " bytes for output");
393 }
394 update(in, inOfs, inLen);
395 byte[] result = doFinal();
396 int n = result.length;
397 System.arraycopy(result, 0, out, outOfs, n);
398 return n;
399 }
400
401 // see JCE spec
402 protected byte[] engineWrap(Key key) throws InvalidKeyException,
403 IllegalBlockSizeException {
404 byte[] encoded = key.getEncoded();
405 if ((encoded == null) || (encoded.length == 0)) {
406 throw new InvalidKeyException("Could not obtain encoded key");
407 }
408 if (encoded.length > buffer.length) {
409 throw new InvalidKeyException("Key is too long for wrapping");
410 }
411 update(encoded, 0, encoded.length);
412 try {
413 return doFinal();
414 } catch (BadPaddingException e) {
415 // should not occur
416 throw new InvalidKeyException("Wrapping failed", e);
417 }
418 }
419
420 // see JCE spec
421 protected Key engineUnwrap(byte[] wrappedKey, String algorithm,
422 int type) throws InvalidKeyException, NoSuchAlgorithmException {
423 if (wrappedKey.length > buffer.length) {
424 throw new InvalidKeyException("Key is too long for unwrapping");
425 }
426 update(wrappedKey, 0, wrappedKey.length);
427 try {
428 byte[] encoded = doFinal();
429 return ConstructKeys.constructKey(encoded, algorithm, type);
430 } catch (BadPaddingException e) {
431 // should not occur
432 throw new InvalidKeyException("Unwrapping failed", e);
433 } catch (IllegalBlockSizeException e) {
434 // should not occur, handled with length check above
435 throw new InvalidKeyException("Unwrapping failed", e);
436 }
437 }
438
439 // see JCE spec
440 protected int engineGetKeySize(Key key) throws InvalidKeyException {
441 RSAKey rsaKey = RSAKeyFactory.toRSAKey(key);
442 return rsaKey.getModulus().bitLength();
443 }
444
445}