1 /*
   2  * Copyright (c) 2003, 2013, 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 sun.security.pkcs11;
  27 
  28 import java.security.*;
  29 import java.security.spec.AlgorithmParameterSpec;
  30 import java.security.spec.*;
  31 
  32 import java.util.Locale;
  33 
  34 import javax.crypto.*;
  35 import javax.crypto.spec.*;
  36 
  37 import static sun.security.pkcs11.TemplateManager.*;
  38 import sun.security.pkcs11.wrapper.*;
  39 import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
  40 import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec;
  41 import sun.security.util.KeyUtil;
  42 
  43 /**
  44  * RSA Cipher implementation class. We currently only support
  45  * PKCS#1 v1.5 padding on top of CKM_RSA_PKCS.
  46  *
  47  * @author  Andreas Sterbenz
  48  * @since   1.5
  49  */
  50 final class P11RSACipher extends CipherSpi {
  51 
  52     // minimum length of PKCS#1 v1.5 padding
  53     private final static int PKCS1_MIN_PADDING_LENGTH = 11;
  54 
  55     // constant byte[] of length 0
  56     private final static byte[] B0 = new byte[0];
  57 
  58     // mode constant for public key encryption
  59     private final static int MODE_ENCRYPT = 1;
  60     // mode constant for private key decryption
  61     private final static int MODE_DECRYPT = 2;
  62     // mode constant for private key encryption (signing)
  63     private final static int MODE_SIGN    = 3;
  64     // mode constant for public key decryption (verifying)
  65     private final static int MODE_VERIFY  = 4;
  66 
  67     // padding type constant for NoPadding
  68     private final static int PAD_NONE = 1;
  69     // padding type constant for PKCS1Padding
  70     private final static int PAD_PKCS1 = 2;
  71 
  72     // token instance
  73     private final Token token;
  74 
  75     // algorithm name (always "RSA")
  76     private final String algorithm;
  77 
  78     // mechanism id
  79     private final long mechanism;
  80 
  81     // associated session, if any
  82     private Session session;
  83 
  84     // mode, one of MODE_* above
  85     private int mode;
  86 
  87     // padding, one of PAD_* above
  88     private int padType;
  89 
  90     private byte[] buffer;
  91     private int bufOfs;
  92 
  93     // key, if init() was called
  94     private P11Key p11Key;
  95 
  96     // flag indicating whether an operation is initialized
  97     private boolean initialized;
  98 
  99     // maximum input data size allowed
 100     // for decryption, this is the length of the key
 101     // for encryption, length of the key minus minimum padding length
 102     private int maxInputSize;
 103 
 104     // maximum output size. this is the length of the key
 105     private int outputSize;
 106 
 107     // cipher parameter for TLS RSA premaster secret
 108     private AlgorithmParameterSpec spec = null;
 109 
 110     // the source of randomness
 111     private SecureRandom random;
 112 
 113     P11RSACipher(Token token, String algorithm, long mechanism)
 114             throws PKCS11Exception {
 115         super();
 116         this.token = token;
 117         this.algorithm = "RSA";
 118         this.mechanism = mechanism;
 119     }
 120 
 121     // modes do not make sense for RSA, but allow ECB
 122     // see JCE spec
 123     protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
 124         if (mode.equalsIgnoreCase("ECB") == false) {
 125             throw new NoSuchAlgorithmException("Unsupported mode " + mode);
 126         }
 127     }
 128 
 129     protected void engineSetPadding(String padding)
 130             throws NoSuchPaddingException {
 131         String lowerPadding = padding.toLowerCase(Locale.ENGLISH);
 132         if (lowerPadding.equals("pkcs1padding")) {
 133             padType = PAD_PKCS1;
 134         } else if (lowerPadding.equals("nopadding")) {
 135             padType = PAD_NONE;
 136         } else {
 137             throw new NoSuchPaddingException("Unsupported padding " + padding);
 138         }
 139     }
 140 
 141     // return 0 as block size, we are not a block cipher
 142     // see JCE spec
 143     protected int engineGetBlockSize() {
 144         return 0;
 145     }
 146 
 147     // return the output size
 148     // see JCE spec
 149     protected int engineGetOutputSize(int inputLen) {
 150         return outputSize;
 151     }
 152 
 153     // no IV, return null
 154     // see JCE spec
 155     protected byte[] engineGetIV() {
 156         return null;
 157     }
 158 
 159     // no parameters, return null
 160     // see JCE spec
 161     protected AlgorithmParameters engineGetParameters() {
 162         return null;
 163     }
 164 
 165     // see JCE spec
 166     protected void engineInit(int opmode, Key key, SecureRandom random)
 167             throws InvalidKeyException {
 168         implInit(opmode, key);
 169     }
 170 
 171     // see JCE spec
 172     protected void engineInit(int opmode, Key key,
 173             AlgorithmParameterSpec params, SecureRandom random)
 174             throws InvalidKeyException, InvalidAlgorithmParameterException {
 175         if (params != null) {
 176             if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) {
 177                 throw new InvalidAlgorithmParameterException(
 178                         "Parameters not supported");
 179             }
 180             spec = params;
 181             this.random = random;   // for TLS RSA premaster secret
 182         }
 183         implInit(opmode, key);
 184     }
 185 
 186     // see JCE spec
 187     protected void engineInit(int opmode, Key key, AlgorithmParameters params,
 188             SecureRandom random)
 189             throws InvalidKeyException, InvalidAlgorithmParameterException {
 190         if (params != null) {
 191             throw new InvalidAlgorithmParameterException(
 192                         "Parameters not supported");
 193         }
 194         implInit(opmode, key);
 195     }
 196 
 197     private void implInit(int opmode, Key key) throws InvalidKeyException {
 198         cancelOperation();
 199         p11Key = P11KeyFactory.convertKey(token, key, algorithm);
 200         boolean encrypt;
 201         if (opmode == Cipher.ENCRYPT_MODE) {
 202             encrypt = true;
 203         } else if (opmode == Cipher.DECRYPT_MODE) {
 204             encrypt = false;
 205         } else if (opmode == Cipher.WRAP_MODE) {
 206             if (p11Key.isPublic() == false) {
 207                 throw new InvalidKeyException
 208                                 ("Wrap has to be used with public keys");
 209             }
 210             // No further setup needed for C_Wrap(). We'll initialize later if
 211             // we can't use C_Wrap().
 212             return;
 213         } else if (opmode == Cipher.UNWRAP_MODE) {
 214             if (p11Key.isPrivate() == false) {
 215                 throw new InvalidKeyException
 216                                 ("Unwrap has to be used with private keys");
 217             }
 218             // No further setup needed for C_Unwrap(). We'll initialize later
 219             // if we can't use C_Unwrap().
 220             return;
 221         } else {
 222             throw new InvalidKeyException("Unsupported mode: " + opmode);
 223         }
 224         if (p11Key.isPublic()) {
 225             mode = encrypt ? MODE_ENCRYPT : MODE_VERIFY;
 226         } else if (p11Key.isPrivate()) {
 227             mode = encrypt ? MODE_SIGN : MODE_DECRYPT;
 228         } else {
 229             throw new InvalidKeyException("Unknown key type: " + p11Key);
 230         }
 231         int n = (p11Key.length() + 7) >> 3;
 232         outputSize = n;
 233         buffer = new byte[n];
 234         maxInputSize = ((padType == PAD_PKCS1 && encrypt) ?
 235                             (n - PKCS1_MIN_PADDING_LENGTH) : n);
 236         try {
 237             initialize();
 238         } catch (PKCS11Exception e) {
 239             throw new InvalidKeyException("init() failed", e);
 240         }
 241     }
 242 
 243     private void cancelOperation() {
 244         token.ensureValid();
 245         if (initialized == false) {
 246             return;
 247         }
 248         initialized = false;
 249         if ((session == null) || (token.explicitCancel == false)) {
 250             return;
 251         }
 252         if (session.hasObjects() == false) {
 253             session = token.killSession(session);
 254             return;
 255         }
 256         try {
 257             PKCS11 p11 = token.p11;
 258             int inLen = maxInputSize;
 259             int outLen = buffer.length;
 260             switch (mode) {
 261             case MODE_ENCRYPT:
 262                 p11.C_Encrypt
 263                         (session.id(), buffer, 0, inLen, buffer, 0, outLen);
 264                 break;
 265             case MODE_DECRYPT:
 266                 p11.C_Decrypt
 267                         (session.id(), buffer, 0, inLen, buffer, 0, outLen);
 268                 break;
 269             case MODE_SIGN:
 270                 byte[] tmpBuffer = new byte[maxInputSize];
 271                 p11.C_Sign
 272                         (session.id(), tmpBuffer);
 273                 break;
 274             case MODE_VERIFY:
 275                 p11.C_VerifyRecover
 276                         (session.id(), buffer, 0, inLen, buffer, 0, outLen);
 277                 break;
 278             default:
 279                 throw new ProviderException("internal error");
 280             }
 281         } catch (PKCS11Exception e) {
 282             // XXX ensure this always works, ignore error
 283         }
 284     }
 285 
 286     private void ensureInitialized() throws PKCS11Exception {
 287         token.ensureValid();
 288         if (initialized == false) {
 289             initialize();
 290         }
 291     }
 292 
 293     private void initialize() throws PKCS11Exception {
 294         if (session == null) {
 295             session = token.getOpSession();
 296         }
 297         PKCS11 p11 = token.p11;
 298         CK_MECHANISM ckMechanism = new CK_MECHANISM(mechanism);
 299         switch (mode) {
 300         case MODE_ENCRYPT:
 301             p11.C_EncryptInit(session.id(), ckMechanism, p11Key.keyID);
 302             break;
 303         case MODE_DECRYPT:
 304             p11.C_DecryptInit(session.id(), ckMechanism, p11Key.keyID);
 305             break;
 306         case MODE_SIGN:
 307             p11.C_SignInit(session.id(), ckMechanism, p11Key.keyID);
 308             break;
 309         case MODE_VERIFY:
 310             p11.C_VerifyRecoverInit(session.id(), ckMechanism, p11Key.keyID);
 311             break;
 312         default:
 313             throw new AssertionError("internal error");
 314         }
 315         bufOfs = 0;
 316         initialized = true;
 317     }
 318 
 319     private void implUpdate(byte[] in, int inOfs, int inLen) {
 320         try {
 321             ensureInitialized();
 322         } catch (PKCS11Exception e) {
 323             throw new ProviderException("update() failed", e);
 324         }
 325         if ((inLen == 0) || (in == null)) {
 326             return;
 327         }
 328         if (bufOfs + inLen > maxInputSize) {
 329             bufOfs = maxInputSize + 1;
 330             return;
 331         }
 332         System.arraycopy(in, inOfs, buffer, bufOfs, inLen);
 333         bufOfs += inLen;
 334     }
 335 
 336     private int implDoFinal(byte[] out, int outOfs, int outLen)
 337             throws BadPaddingException, IllegalBlockSizeException {
 338         if (bufOfs > maxInputSize) {
 339             throw new IllegalBlockSizeException("Data must not be longer "
 340                 + "than " + maxInputSize + " bytes");
 341         }
 342         try {
 343             ensureInitialized();
 344             PKCS11 p11 = token.p11;
 345             int n;
 346             switch (mode) {
 347             case MODE_ENCRYPT:
 348                 n = p11.C_Encrypt
 349                         (session.id(), buffer, 0, bufOfs, out, outOfs, outLen);
 350                 break;
 351             case MODE_DECRYPT:
 352                 n = p11.C_Decrypt
 353                         (session.id(), buffer, 0, bufOfs, out, outOfs, outLen);
 354                 break;
 355             case MODE_SIGN:
 356                 byte[] tmpBuffer = new byte[bufOfs];
 357                 System.arraycopy(buffer, 0, tmpBuffer, 0, bufOfs);
 358                 tmpBuffer = p11.C_Sign(session.id(), tmpBuffer);
 359                 if (tmpBuffer.length > outLen) {
 360                     throw new BadPaddingException("Output buffer too small");
 361                 }
 362                 System.arraycopy(tmpBuffer, 0, out, outOfs, tmpBuffer.length);
 363                 n = tmpBuffer.length;
 364                 break;
 365             case MODE_VERIFY:
 366                 n = p11.C_VerifyRecover
 367                         (session.id(), buffer, 0, bufOfs, out, outOfs, outLen);
 368                 break;
 369             default:
 370                 throw new ProviderException("internal error");
 371             }
 372             return n;
 373         } catch (PKCS11Exception e) {
 374             throw (BadPaddingException)new BadPaddingException
 375                 ("doFinal() failed").initCause(e);
 376         } finally {
 377             initialized = false;
 378             session = token.releaseSession(session);
 379         }
 380     }
 381 
 382     // see JCE spec
 383     protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
 384         implUpdate(in, inOfs, inLen);
 385         return B0;
 386     }
 387 
 388     // see JCE spec
 389     protected int engineUpdate(byte[] in, int inOfs, int inLen,
 390             byte[] out, int outOfs) throws ShortBufferException {
 391         implUpdate(in, inOfs, inLen);
 392         return 0;
 393     }
 394 
 395     // see JCE spec
 396     protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
 397             throws IllegalBlockSizeException, BadPaddingException {
 398         implUpdate(in, inOfs, inLen);
 399         int n = implDoFinal(buffer, 0, buffer.length);
 400         byte[] out = new byte[n];
 401         System.arraycopy(buffer, 0, out, 0, n);
 402         return out;
 403     }
 404 
 405     // see JCE spec
 406     protected int engineDoFinal(byte[] in, int inOfs, int inLen,
 407             byte[] out, int outOfs) throws ShortBufferException,
 408             IllegalBlockSizeException, BadPaddingException {
 409         implUpdate(in, inOfs, inLen);
 410         return implDoFinal(out, outOfs, out.length - outOfs);
 411     }
 412 
 413     private byte[] doFinal() throws BadPaddingException,
 414             IllegalBlockSizeException {
 415         byte[] t = new byte[2048];
 416         int n = implDoFinal(t, 0, t.length);
 417         byte[] out = new byte[n];
 418         System.arraycopy(t, 0, out, 0, n);
 419         return out;
 420     }
 421 
 422     // see JCE spec
 423     protected byte[] engineWrap(Key key) throws InvalidKeyException,
 424             IllegalBlockSizeException {
 425         String keyAlg = key.getAlgorithm();
 426         P11Key sKey = null;
 427         try {
 428             // The conversion may fail, e.g. trying to wrap an AES key on
 429             // a token that does not support AES, or when the key size is
 430             // not within the range supported by the token.
 431             sKey = P11SecretKeyFactory.convertKey(token, key, keyAlg);
 432         } catch (InvalidKeyException ike) {
 433             byte[] toBeWrappedKey = key.getEncoded();
 434             if (toBeWrappedKey == null) {
 435                 throw new InvalidKeyException
 436                         ("wrap() failed, no encoding available", ike);
 437             }
 438             // Directly encrypt the key encoding when key conversion failed
 439             implInit(Cipher.ENCRYPT_MODE, p11Key);
 440             implUpdate(toBeWrappedKey, 0, toBeWrappedKey.length);
 441             try {
 442                 return doFinal();
 443             } catch (BadPaddingException bpe) {
 444                 // should not occur
 445                 throw new InvalidKeyException("wrap() failed", bpe);
 446             } finally {
 447                 // Restore original mode
 448                 implInit(Cipher.WRAP_MODE, p11Key);
 449             }
 450         }
 451         Session s = null;
 452         try {
 453             s = token.getOpSession();
 454             return token.p11.C_WrapKey(s.id(), new CK_MECHANISM(mechanism),
 455                 p11Key.keyID, sKey.keyID);
 456         } catch (PKCS11Exception e) {
 457             throw new InvalidKeyException("wrap() failed", e);
 458         } finally {
 459             token.releaseSession(s);
 460         }
 461     }
 462 
 463     // see JCE spec
 464     protected Key engineUnwrap(byte[] wrappedKey, String algorithm,
 465             int type) throws InvalidKeyException, NoSuchAlgorithmException {
 466 
 467         boolean isTlsRsaPremasterSecret =
 468                 algorithm.equals("TlsRsaPremasterSecret");
 469         Exception failover = null;
 470 
 471         SecureRandom secureRandom = random;
 472         if (secureRandom == null && isTlsRsaPremasterSecret) {
 473             secureRandom = new SecureRandom();
 474         }
 475 
 476         // Should C_Unwrap be preferred for non-TLS RSA premaster secret?
 477         if (token.supportsRawSecretKeyImport()) {
 478             // XXX implement unwrap using C_Unwrap() for all keys
 479             implInit(Cipher.DECRYPT_MODE, p11Key);
 480             if (wrappedKey.length > maxInputSize) {
 481                 throw new InvalidKeyException("Key is too long for unwrapping");
 482             }
 483 
 484             byte[] encoded = null;
 485             implUpdate(wrappedKey, 0, wrappedKey.length);
 486             try {
 487                 encoded = doFinal();
 488             } catch (BadPaddingException e) {
 489                 if (isTlsRsaPremasterSecret) {
 490                     failover = e;
 491                 } else {
 492                     throw new InvalidKeyException("Unwrapping failed", e);
 493                 }
 494             } catch (IllegalBlockSizeException e) {
 495                 // should not occur, handled with length check above
 496                 throw new InvalidKeyException("Unwrapping failed", e);
 497             }
 498 
 499             if (isTlsRsaPremasterSecret) {
 500                 if (!(spec instanceof TlsRsaPremasterSecretParameterSpec)) {
 501                     throw new IllegalStateException(
 502                             "No TlsRsaPremasterSecretParameterSpec specified");
 503                 }
 504 
 505                 // polish the TLS premaster secret
 506                 TlsRsaPremasterSecretParameterSpec psps =
 507                         (TlsRsaPremasterSecretParameterSpec)spec;
 508                 encoded = KeyUtil.checkTlsPreMasterSecretKey(
 509                         psps.getClientVersion(), psps.getServerVersion(),
 510                         secureRandom, encoded, (failover != null));
 511             }
 512 
 513             return ConstructKeys.constructKey(encoded, algorithm, type);
 514         } else {
 515             Session s = null;
 516             SecretKey secretKey = null;
 517             try {
 518                 try {
 519                     s = token.getObjSession();
 520                     long keyType = CKK_GENERIC_SECRET;
 521                     CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 522                             new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
 523                             new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType),
 524                         };
 525                     attributes = token.getAttributes(
 526                             O_IMPORT, CKO_SECRET_KEY, keyType, attributes);
 527                     long keyID = token.p11.C_UnwrapKey(s.id(),
 528                             new CK_MECHANISM(mechanism), p11Key.keyID,
 529                             wrappedKey, attributes);
 530                     secretKey = P11Key.secretKey(s, keyID,
 531                             algorithm, 48 << 3, attributes);
 532                 } catch (PKCS11Exception e) {
 533                     if (isTlsRsaPremasterSecret) {
 534                         failover = e;
 535                     } else {
 536                         throw new InvalidKeyException("unwrap() failed", e);
 537                     }
 538                 }
 539 
 540                 if (isTlsRsaPremasterSecret) {
 541                     byte[] replacer = new byte[48];
 542                     if (failover == null) {
 543                         // Does smart compiler dispose this operation?
 544                         secureRandom.nextBytes(replacer);
 545                     }
 546 
 547                     TlsRsaPremasterSecretParameterSpec psps =
 548                             (TlsRsaPremasterSecretParameterSpec)spec;
 549 
 550                     // Please use the tricky failover and replacer byte array
 551                     // as the parameters so that smart compiler won't dispose
 552                     // the unused variable .
 553                     secretKey = polishPreMasterSecretKey(token, s,
 554                             failover, replacer, secretKey,
 555                             psps.getClientVersion(), psps.getServerVersion());
 556                 }
 557 
 558                 return secretKey;
 559             } finally {
 560                 token.releaseSession(s);
 561             }
 562         }
 563     }
 564 
 565     // see JCE spec
 566     protected int engineGetKeySize(Key key) throws InvalidKeyException {
 567         int n = P11KeyFactory.convertKey(token, key, algorithm).length();
 568         return n;
 569     }
 570 
 571     private static SecretKey polishPreMasterSecretKey(
 572             Token token, Session session,
 573             Exception failover, byte[] replacer, SecretKey secretKey,
 574             int clientVersion, int serverVersion) {
 575 
 576         if (failover != null) {
 577             CK_VERSION version = new CK_VERSION(
 578                     (clientVersion >>> 8) & 0xFF, clientVersion & 0xFF);
 579             try {
 580                 CK_ATTRIBUTE[] attributes = token.getAttributes(
 581                         O_GENERATE, CKO_SECRET_KEY,
 582                         CKK_GENERIC_SECRET, new CK_ATTRIBUTE[0]);
 583                 long keyID = token.p11.C_GenerateKey(session.id(),
 584                     // new CK_MECHANISM(CKM_TLS_PRE_MASTER_KEY_GEN, version),
 585                         new CK_MECHANISM(CKM_SSL3_PRE_MASTER_KEY_GEN, version),
 586                         attributes);
 587                 return P11Key.secretKey(session,
 588                         keyID, "TlsRsaPremasterSecret", 48 << 3, attributes);
 589             } catch (PKCS11Exception e) {
 590                 throw new ProviderException(
 591                         "Could not generate premaster secret", e);
 592             }
 593         }
 594 
 595         return secretKey;
 596     }
 597 
 598 }
 599 
 600 final class ConstructKeys {
 601     /**
 602      * Construct a public key from its encoding.
 603      *
 604      * @param encodedKey the encoding of a public key.
 605      *
 606      * @param encodedKeyAlgorithm the algorithm the encodedKey is for.
 607      *
 608      * @return a public key constructed from the encodedKey.
 609      */
 610     private static final PublicKey constructPublicKey(byte[] encodedKey,
 611             String encodedKeyAlgorithm)
 612             throws InvalidKeyException, NoSuchAlgorithmException {
 613         try {
 614             KeyFactory keyFactory =
 615                 KeyFactory.getInstance(encodedKeyAlgorithm);
 616             X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey);
 617             return keyFactory.generatePublic(keySpec);
 618         } catch (NoSuchAlgorithmException nsae) {
 619             throw new NoSuchAlgorithmException("No installed providers " +
 620                                                "can create keys for the " +
 621                                                encodedKeyAlgorithm +
 622                                                "algorithm", nsae);
 623         } catch (InvalidKeySpecException ike) {
 624             throw new InvalidKeyException("Cannot construct public key", ike);
 625         }
 626     }
 627 
 628     /**
 629      * Construct a private key from its encoding.
 630      *
 631      * @param encodedKey the encoding of a private key.
 632      *
 633      * @param encodedKeyAlgorithm the algorithm the wrapped key is for.
 634      *
 635      * @return a private key constructed from the encodedKey.
 636      */
 637     private static final PrivateKey constructPrivateKey(byte[] encodedKey,
 638             String encodedKeyAlgorithm) throws InvalidKeyException,
 639             NoSuchAlgorithmException {
 640         try {
 641             KeyFactory keyFactory =
 642                 KeyFactory.getInstance(encodedKeyAlgorithm);
 643             PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
 644             return keyFactory.generatePrivate(keySpec);
 645         } catch (NoSuchAlgorithmException nsae) {
 646             throw new NoSuchAlgorithmException("No installed providers " +
 647                                                "can create keys for the " +
 648                                                encodedKeyAlgorithm +
 649                                                "algorithm", nsae);
 650         } catch (InvalidKeySpecException ike) {
 651             throw new InvalidKeyException("Cannot construct private key", ike);
 652         }
 653     }
 654 
 655     /**
 656      * Construct a secret key from its encoding.
 657      *
 658      * @param encodedKey the encoding of a secret key.
 659      *
 660      * @param encodedKeyAlgorithm the algorithm the secret key is for.
 661      *
 662      * @return a secret key constructed from the encodedKey.
 663      */
 664     private static final SecretKey constructSecretKey(byte[] encodedKey,
 665             String encodedKeyAlgorithm) {
 666         return new SecretKeySpec(encodedKey, encodedKeyAlgorithm);
 667     }
 668 
 669     static final Key constructKey(byte[] encoding, String keyAlgorithm,
 670             int keyType) throws InvalidKeyException, NoSuchAlgorithmException {
 671         switch (keyType) {
 672         case Cipher.SECRET_KEY:
 673             return constructSecretKey(encoding, keyAlgorithm);
 674         case Cipher.PRIVATE_KEY:
 675             return constructPrivateKey(encoding, keyAlgorithm);
 676         case Cipher.PUBLIC_KEY:
 677             return constructPublicKey(encoding, keyAlgorithm);
 678         default:
 679             throw new InvalidKeyException("Unknown keytype " + keyType);
 680         }
 681     }
 682 }