2
|
1 |
/*
|
|
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 |
|
|
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 |
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 |
}
|