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 }