blob: e2060c739fa75836fc2a7e68143aa15c17293ee8 [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 sun.security.pkcs11;
27
28import java.security.*;
29import java.security.spec.AlgorithmParameterSpec;
30import java.security.spec.*;
31
32import javax.crypto.*;
33import javax.crypto.spec.*;
34
35import static sun.security.pkcs11.TemplateManager.*;
36import sun.security.pkcs11.wrapper.*;
37import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
38
39/**
40 * RSA Cipher implementation class. We currently only support
41 * PKCS#1 v1.5 padding on top of CKM_RSA_PKCS.
42 *
43 * @author Andreas Sterbenz
44 * @since 1.5
45 */
46final class P11RSACipher extends CipherSpi {
47
48 // minimum length of PKCS#1 v1.5 padding
49 private final static int PKCS1_MIN_PADDING_LENGTH = 11;
50
51 // constant byte[] of length 0
52 private final static byte[] B0 = new byte[0];
53
54 // mode constant for public key encryption
55 private final static int MODE_ENCRYPT = 1;
56 // mode constant for private key decryption
57 private final static int MODE_DECRYPT = 2;
58 // mode constant for private key encryption (signing)
59 private final static int MODE_SIGN = 3;
60 // mode constant for public key decryption (verifying)
61 private final static int MODE_VERIFY = 4;
62
63 // token instance
64 private final Token token;
65
66 // algorithm name (always "RSA")
67 private final String algorithm;
68
69 // mechanism id
70 private final long mechanism;
71
72 // associated session, if any
73 private Session session;
74
75 // mode, one of MODE_* above
76 private int mode;
77
78 private byte[] buffer;
79 private int bufOfs;
80
81 // key, if init() was called
82 private P11Key p11Key;
83
84 // flag indicating whether an operation is initialized
85 private boolean initialized;
86
87 // maximum input data size allowed
88 // for decryption, this is the length of the key
89 // for encryption, length of the key minus minimum padding length
90 private int maxInputSize;
91
92 // maximum output size. this is the length of the key
93 private int outputSize;
94
95 P11RSACipher(Token token, String algorithm, long mechanism)
96 throws PKCS11Exception {
97 super();
98 this.token = token;
99 this.algorithm = "RSA";
100 this.mechanism = mechanism;
101 session = token.getOpSession();
102 }
103
104 // modes do not make sense for RSA, but allow ECB
105 // see JCE spec
106 protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
107 if (mode.equalsIgnoreCase("ECB") == false) {
108 throw new NoSuchAlgorithmException("Unsupported mode " + mode);
109 }
110 }
111
112 protected void engineSetPadding(String padding)
113 throws NoSuchPaddingException {
114 String lowerPadding = padding.toLowerCase();
115 if (lowerPadding.equals("pkcs1Padding")) {
116 // empty
117 } else {
118 throw new NoSuchPaddingException("Unsupported padding " + padding);
119 }
120 }
121
122 // return 0 as block size, we are not a block cipher
123 // see JCE spec
124 protected int engineGetBlockSize() {
125 return 0;
126 }
127
128 // return the output size
129 // see JCE spec
130 protected int engineGetOutputSize(int inputLen) {
131 return outputSize;
132 }
133
134 // no IV, return null
135 // see JCE spec
136 protected byte[] engineGetIV() {
137 return null;
138 }
139
140 // no parameters, return null
141 // see JCE spec
142 protected AlgorithmParameters engineGetParameters() {
143 return null;
144 }
145
146 // see JCE spec
147 protected void engineInit(int opmode, Key key, SecureRandom random)
148 throws InvalidKeyException {
149 implInit(opmode, key);
150 }
151
152 // see JCE spec
153 protected void engineInit(int opmode, Key key,
154 AlgorithmParameterSpec params, SecureRandom random)
155 throws InvalidKeyException, InvalidAlgorithmParameterException {
156 if (params != null) {
157 throw new InvalidAlgorithmParameterException
158 ("Parameters not supported");
159 }
160 implInit(opmode, key);
161 }
162
163 // see JCE spec
164 protected void engineInit(int opmode, Key key, AlgorithmParameters params,
165 SecureRandom random)
166 throws InvalidKeyException, InvalidAlgorithmParameterException {
167 if (params != null) {
168 throw new InvalidAlgorithmParameterException
169 ("Parameters not supported");
170 }
171 implInit(opmode, key);
172 }
173
174 private void implInit(int opmode, Key key) throws InvalidKeyException {
175 cancelOperation();
176 p11Key = P11KeyFactory.convertKey(token, key, algorithm);
177 boolean encrypt;
178 if (opmode == Cipher.ENCRYPT_MODE) {
179 encrypt = true;
180 } else if (opmode == Cipher.DECRYPT_MODE) {
181 encrypt = false;
182 } else if (opmode == Cipher.WRAP_MODE) {
183 if (p11Key.isPublic() == false) {
184 throw new InvalidKeyException
185 ("Wrap has to be used with public keys");
186 }
187 // No further setup needed for C_Wrap(). We remain uninitialized.
188 return;
189 } else if (opmode == Cipher.UNWRAP_MODE) {
190 if (p11Key.isPrivate() == false) {
191 throw new InvalidKeyException
192 ("Unwrap has to be used with private keys");
193 }
194 encrypt = false;
195 } else {
196 throw new InvalidKeyException("Unsupported mode: " + opmode);
197 }
198 if (p11Key.isPublic()) {
199 mode = encrypt ? MODE_ENCRYPT : MODE_VERIFY;
200 } else if (p11Key.isPrivate()) {
201 mode = encrypt ? MODE_SIGN : MODE_DECRYPT;
202 } else {
203 throw new InvalidKeyException("Unknown key type: " + p11Key);
204 }
205 int n = (p11Key.keyLength() + 7) >> 3;
206 outputSize = n;
207 buffer = new byte[n];
208 maxInputSize = encrypt ? (n - PKCS1_MIN_PADDING_LENGTH) : n;
209 try {
210 initialize();
211 } catch (PKCS11Exception e) {
212 throw new InvalidKeyException("init() failed", e);
213 }
214 }
215
216 private void cancelOperation() {
217 token.ensureValid();
218 if (initialized == false) {
219 return;
220 }
221 initialized = false;
222 if ((session == null) || (token.explicitCancel == false)) {
223 return;
224 }
225 if (session.hasObjects() == false) {
226 session = token.killSession(session);
227 return;
228 }
229 try {
230 PKCS11 p11 = token.p11;
231 int inLen = maxInputSize;
232 int outLen = buffer.length;
233 switch (mode) {
234 case MODE_ENCRYPT:
235 p11.C_Encrypt
236 (session.id(), buffer, 0, inLen, buffer, 0, outLen);
237 break;
238 case MODE_DECRYPT:
239 p11.C_Decrypt
240 (session.id(), buffer, 0, inLen, buffer, 0, outLen);
241 break;
242 case MODE_SIGN:
243 byte[] tmpBuffer = new byte[maxInputSize];
244 p11.C_Sign
245 (session.id(), tmpBuffer);
246 break;
247 case MODE_VERIFY:
248 p11.C_VerifyRecover
249 (session.id(), buffer, 0, inLen, buffer, 0, outLen);
250 break;
251 default:
252 throw new ProviderException("internal error");
253 }
254 } catch (PKCS11Exception e) {
255 // XXX ensure this always works, ignore error
256 }
257 }
258
259 private void ensureInitialized() throws PKCS11Exception {
260 token.ensureValid();
261 if (initialized == false) {
262 initialize();
263 }
264 }
265
266 private void initialize() throws PKCS11Exception {
267 if (session == null) {
268 session = token.getOpSession();
269 }
270 PKCS11 p11 = token.p11;
271 CK_MECHANISM ckMechanism = new CK_MECHANISM(mechanism);
272 switch (mode) {
273 case MODE_ENCRYPT:
274 p11.C_EncryptInit(session.id(), ckMechanism, p11Key.keyID);
275 break;
276 case MODE_DECRYPT:
277 p11.C_DecryptInit(session.id(), ckMechanism, p11Key.keyID);
278 break;
279 case MODE_SIGN:
280 p11.C_SignInit(session.id(), ckMechanism, p11Key.keyID);
281 break;
282 case MODE_VERIFY:
283 p11.C_VerifyRecoverInit(session.id(), ckMechanism, p11Key.keyID);
284 break;
285 default:
286 throw new AssertionError("internal error");
287 }
288 bufOfs = 0;
289 initialized = true;
290 }
291
292 private void implUpdate(byte[] in, int inOfs, int inLen) {
293 try {
294 ensureInitialized();
295 } catch (PKCS11Exception e) {
296 throw new ProviderException("update() failed", e);
297 }
298 if ((inLen == 0) || (in == null)) {
299 return;
300 }
301 if (bufOfs + inLen > maxInputSize) {
302 bufOfs = maxInputSize + 1;
303 return;
304 }
305 System.arraycopy(in, inOfs, buffer, bufOfs, inLen);
306 bufOfs += inLen;
307 }
308
309 private int implDoFinal(byte[] out, int outOfs, int outLen)
310 throws BadPaddingException, IllegalBlockSizeException {
311 if (bufOfs > maxInputSize) {
312 throw new IllegalBlockSizeException("Data must not be longer "
313 + "than " + maxInputSize + " bytes");
314 }
315 try {
316 ensureInitialized();
317 PKCS11 p11 = token.p11;
318 int n;
319 switch (mode) {
320 case MODE_ENCRYPT:
321 n = p11.C_Encrypt
322 (session.id(), buffer, 0, bufOfs, out, outOfs, outLen);
323 break;
324 case MODE_DECRYPT:
325 n = p11.C_Decrypt
326 (session.id(), buffer, 0, bufOfs, out, outOfs, outLen);
327 break;
328 case MODE_SIGN:
329 byte[] tmpBuffer = new byte[bufOfs];
330 System.arraycopy(buffer, 0, tmpBuffer, 0, bufOfs);
331 tmpBuffer = p11.C_Sign(session.id(), tmpBuffer);
332 if (tmpBuffer.length > outLen) {
333 throw new BadPaddingException("Output buffer too small");
334 }
335 System.arraycopy(tmpBuffer, 0, out, outOfs, tmpBuffer.length);
336 n = tmpBuffer.length;
337 break;
338 case MODE_VERIFY:
339 n = p11.C_VerifyRecover
340 (session.id(), buffer, 0, bufOfs, out, outOfs, outLen);
341 break;
342 default:
343 throw new ProviderException("internal error");
344 }
345 return n;
346 } catch (PKCS11Exception e) {
347 throw (BadPaddingException)new BadPaddingException
348 ("doFinal() failed").initCause(e);
349 } finally {
350 initialized = false;
351 session = token.releaseSession(session);
352 }
353 }
354
355 // see JCE spec
356 protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
357 implUpdate(in, inOfs, inLen);
358 return B0;
359 }
360
361 // see JCE spec
362 protected int engineUpdate(byte[] in, int inOfs, int inLen,
363 byte[] out, int outOfs) throws ShortBufferException {
364 implUpdate(in, inOfs, inLen);
365 return 0;
366 }
367
368 // see JCE spec
369 protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
370 throws IllegalBlockSizeException, BadPaddingException {
371 implUpdate(in, inOfs, inLen);
372 int n = implDoFinal(buffer, 0, buffer.length);
373 byte[] out = new byte[n];
374 System.arraycopy(buffer, 0, out, 0, n);
375 return out;
376 }
377
378 // see JCE spec
379 protected int engineDoFinal(byte[] in, int inOfs, int inLen,
380 byte[] out, int outOfs) throws ShortBufferException,
381 IllegalBlockSizeException, BadPaddingException {
382 implUpdate(in, inOfs, inLen);
383 return implDoFinal(out, outOfs, out.length - outOfs);
384 }
385
386 private byte[] doFinal() throws BadPaddingException, IllegalBlockSizeException {
387 byte[] t = new byte[2048];
388 int n = implDoFinal(t, 0, t.length);
389 byte[] out = new byte[n];
390 System.arraycopy(t, 0, out, 0, n);
391 return out;
392 }
393
394 // see JCE spec
395 protected byte[] engineWrap(Key key) throws InvalidKeyException,
396 IllegalBlockSizeException {
397 // XXX Note that if we cannot convert key to a key on this token,
398 // we will fail. For example, trying a wrap an AES key on a token that
399 // does not support AES.
400 // We could implement a fallback that just encrypts the encoding
401 // (assuming the key is not sensitive). For now, we are operating under
402 // the assumption that this is not necessary.
403 String keyAlg = key.getAlgorithm();
404 P11Key secretKey = P11SecretKeyFactory.convertKey(token, key, keyAlg);
405 Session s = null;
406 try {
407 s = token.getOpSession();
408 byte[] b = token.p11.C_WrapKey(s.id(), new CK_MECHANISM(mechanism),
409 p11Key.keyID, secretKey.keyID);
410 return b;
411 } catch (PKCS11Exception e) {
412 throw new InvalidKeyException("wrap() failed", e);
413 } finally {
414 token.releaseSession(s);
415 }
416 }
417
418 // see JCE spec
419 protected Key engineUnwrap(byte[] wrappedKey, String algorithm,
420 int type) throws InvalidKeyException, NoSuchAlgorithmException {
421 if (algorithm.equals("TlsRsaPremasterSecret")) {
422 // the instance variable "session" has been initialized for
423 // decrypt mode, so use a local variable instead.
424 Session s = null;
425 try {
426 s = token.getObjSession();
427 long keyType = CKK_GENERIC_SECRET;
428 CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
429 new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
430 new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType),
431 };
432 attributes = token.getAttributes
433 (O_IMPORT, CKO_SECRET_KEY, keyType, attributes);
434 long keyID = token.p11.C_UnwrapKey(s.id(), new CK_MECHANISM(mechanism),
435 p11Key.keyID, wrappedKey, attributes);
436 return P11Key.secretKey(session, keyID, algorithm, 48 << 3, attributes);
437 } catch (PKCS11Exception e) {
438 throw new InvalidKeyException("wrap() failed", e);
439 } finally {
440 token.releaseSession(s);
441 }
442 }
443 // XXX implement unwrap using C_Unwrap() for all keys
444 if (wrappedKey.length > maxInputSize) {
445 throw new InvalidKeyException("Key is too long for unwrapping");
446 }
447 implUpdate(wrappedKey, 0, wrappedKey.length);
448 try {
449 byte[] encoded = doFinal();
450 return ConstructKeys.constructKey(encoded, algorithm, type);
451 } catch (BadPaddingException e) {
452 // should not occur
453 throw new InvalidKeyException("Unwrapping failed", e);
454 } catch (IllegalBlockSizeException e) {
455 // should not occur, handled with length check above
456 throw new InvalidKeyException("Unwrapping failed", e);
457 }
458 }
459
460 // see JCE spec
461 protected int engineGetKeySize(Key key) throws InvalidKeyException {
462 int n = P11KeyFactory.convertKey(token, key, algorithm).keyLength();
463 return n;
464 }
465
466 protected void finalize() throws Throwable {
467 try {
468 if ((session != null) && token.isValid()) {
469 cancelOperation();
470 session = token.releaseSession(session);
471 }
472 } finally {
473 super.finalize();
474 }
475 }
476
477}
478
479final class ConstructKeys {
480 /**
481 * Construct a public key from its encoding.
482 *
483 * @param encodedKey the encoding of a public key.
484 *
485 * @param encodedKeyAlgorithm the algorithm the encodedKey is for.
486 *
487 * @return a public key constructed from the encodedKey.
488 */
489 private static final PublicKey constructPublicKey(byte[] encodedKey,
490 String encodedKeyAlgorithm)
491 throws InvalidKeyException, NoSuchAlgorithmException {
492 try {
493 KeyFactory keyFactory =
494 KeyFactory.getInstance(encodedKeyAlgorithm);
495 X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey);
496 return keyFactory.generatePublic(keySpec);
497 } catch (NoSuchAlgorithmException nsae) {
498 throw new NoSuchAlgorithmException("No installed providers " +
499 "can create keys for the " +
500 encodedKeyAlgorithm +
501 "algorithm", nsae);
502 } catch (InvalidKeySpecException ike) {
503 throw new InvalidKeyException("Cannot construct public key", ike);
504 }
505 }
506
507 /**
508 * Construct a private key from its encoding.
509 *
510 * @param encodedKey the encoding of a private key.
511 *
512 * @param encodedKeyAlgorithm the algorithm the wrapped key is for.
513 *
514 * @return a private key constructed from the encodedKey.
515 */
516 private static final PrivateKey constructPrivateKey(byte[] encodedKey,
517 String encodedKeyAlgorithm) throws InvalidKeyException,
518 NoSuchAlgorithmException {
519 try {
520 KeyFactory keyFactory =
521 KeyFactory.getInstance(encodedKeyAlgorithm);
522 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
523 return keyFactory.generatePrivate(keySpec);
524 } catch (NoSuchAlgorithmException nsae) {
525 throw new NoSuchAlgorithmException("No installed providers " +
526 "can create keys for the " +
527 encodedKeyAlgorithm +
528 "algorithm", nsae);
529 } catch (InvalidKeySpecException ike) {
530 throw new InvalidKeyException("Cannot construct private key", ike);
531 }
532 }
533
534 /**
535 * Construct a secret key from its encoding.
536 *
537 * @param encodedKey the encoding of a secret key.
538 *
539 * @param encodedKeyAlgorithm the algorithm the secret key is for.
540 *
541 * @return a secret key constructed from the encodedKey.
542 */
543 private static final SecretKey constructSecretKey(byte[] encodedKey,
544 String encodedKeyAlgorithm) {
545 return new SecretKeySpec(encodedKey, encodedKeyAlgorithm);
546 }
547
548 static final Key constructKey(byte[] encoding, String keyAlgorithm,
549 int keyType) throws InvalidKeyException, NoSuchAlgorithmException {
550 switch (keyType) {
551 case Cipher.SECRET_KEY:
552 return constructSecretKey(encoding, keyAlgorithm);
553 case Cipher.PRIVATE_KEY:
554 return constructPrivateKey(encoding, keyAlgorithm);
555 case Cipher.PUBLIC_KEY:
556 return constructPublicKey(encoding, keyAlgorithm);
557 default:
558 throw new InvalidKeyException("Unknown keytype " + keyType);
559 }
560 }
561}