1 /*
   2  * Copyright (c) 2003, 2015, 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     @SuppressWarnings("deprecation")
 173     protected void engineInit(int opmode, Key key,
 174             AlgorithmParameterSpec params, SecureRandom random)
 175             throws InvalidKeyException, InvalidAlgorithmParameterException {
 176         if (params != null) {
 177             if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) {
 178                 throw new InvalidAlgorithmParameterException(
 179                         "Parameters not supported");
 180             }
 181             spec = params;
 182             this.random = random;   // for TLS RSA premaster secret
 183         }
 184         implInit(opmode, key);
 185     }
 186 
 187     // see JCE spec
 188     protected void engineInit(int opmode, Key key, AlgorithmParameters params,
 189             SecureRandom random)
 190             throws InvalidKeyException, InvalidAlgorithmParameterException {
 191         if (params != null) {
 192             throw new InvalidAlgorithmParameterException(
 193                         "Parameters not supported");
 194         }
 195         implInit(opmode, key);
 196     }
 197 
 198     private void implInit(int opmode, Key key) throws InvalidKeyException {
 199         reset(true);
 200         p11Key = P11KeyFactory.convertKey(token, key, algorithm);
 201         boolean encrypt;
 202         if (opmode == Cipher.ENCRYPT_MODE) {
 203             encrypt = true;
 204         } else if (opmode == Cipher.DECRYPT_MODE) {
 205             encrypt = false;
 206         } else if (opmode == Cipher.WRAP_MODE) {
 207             if (p11Key.isPublic() == false) {
 208                 throw new InvalidKeyException
 209                                 ("Wrap has to be used with public keys");
 210             }
 211             // No further setup needed for C_Wrap(). We'll initialize later if
 212             // we can't use C_Wrap().
 213             return;
 214         } else if (opmode == Cipher.UNWRAP_MODE) {
 215             if (p11Key.isPrivate() == false) {
 216                 throw new InvalidKeyException
 217                                 ("Unwrap has to be used with private keys");
 218             }
 219             // No further setup needed for C_Unwrap(). We'll initialize later
 220             // if we can't use C_Unwrap().
 221             return;
 222         } else {
 223             throw new InvalidKeyException("Unsupported mode: " + opmode);
 224         }
 225         if (p11Key.isPublic()) {
 226             mode = encrypt ? MODE_ENCRYPT : MODE_VERIFY;
 227         } else if (p11Key.isPrivate()) {
 228             mode = encrypt ? MODE_SIGN : MODE_DECRYPT;
 229         } else {
 230             throw new InvalidKeyException("Unknown key type: " + p11Key);
 231         }
 232         int n = (p11Key.length() + 7) >> 3;
 233         outputSize = n;
 234         buffer = new byte[n];
 235         maxInputSize = ((padType == PAD_PKCS1 && encrypt) ?
 236                             (n - PKCS1_MIN_PADDING_LENGTH) : n);
 237         try {
 238             ensureInitialized();
 239         } catch (PKCS11Exception e) {
 240             throw new InvalidKeyException("init() failed", e);
 241         }
 242     }
 243 
 244     // reset the states to the pre-initialized values
 245     private void reset(boolean doCancel) {
 246         if (!initialized) {
 247             return;
 248         }
 249         initialized = false;
 250         try {
 251             if (session == null) {
 252                 return;
 253             }
 254             if (doCancel && token.explicitCancel) {
 255                 cancelOperation();
 256             }
 257         } finally {
 258             p11Key.decNativeKeyRef();
 259             session = token.releaseSession(session);
 260         }
 261     }
 262 
 263     private void cancelOperation() {
 264         token.ensureValid();
 265         if (session.hasObjects() == false) {
 266             session = token.killSession(session);
 267             return;
 268         } else {
 269             try {
 270                 PKCS11 p11 = token.p11;
 271                 int inLen = maxInputSize;
 272                 int outLen = buffer.length;
 273                 switch (mode) {
 274                 case MODE_ENCRYPT:
 275                     p11.C_Encrypt
 276                             (session.id(), buffer, 0, inLen, buffer, 0, outLen);
 277                     break;
 278                 case MODE_DECRYPT:
 279                     p11.C_Decrypt
 280                             (session.id(), buffer, 0, inLen, buffer, 0, outLen);
 281                     break;
 282                 case MODE_SIGN:
 283                     byte[] tmpBuffer = new byte[maxInputSize];
 284                     p11.C_Sign
 285                             (session.id(), tmpBuffer);
 286                     break;
 287                 case MODE_VERIFY:
 288                     p11.C_VerifyRecover
 289                             (session.id(), buffer, 0, inLen, buffer, 0, outLen);
 290                     break;
 291                 default:
 292                     throw new ProviderException("internal error");
 293                 }
 294             } catch (PKCS11Exception e) {
 295                 // XXX ensure this always works, ignore error
 296             }
 297         }
 298     }
 299 
 300     private void ensureInitialized() throws PKCS11Exception {
 301         if (initialized) {
 302             return;
 303         }
 304         if (p11Key == null) {
 305             throw new ProviderException(
 306                     "Operation cannot be performed without calling engineInit first");
 307         }
 308         token.ensureValid();
 309         p11Key.incNativeKeyRef();
 310         try {
 311             if (session == null) {
 312                 session = token.getOpSession();
 313             }
 314             PKCS11 p11 = token.p11;
 315             CK_MECHANISM ckMechanism = new CK_MECHANISM(mechanism);
 316             switch (mode) {
 317             case MODE_ENCRYPT:
 318                 p11.C_EncryptInit(session.id(), ckMechanism, p11Key.keyID);
 319                 break;
 320             case MODE_DECRYPT:
 321                 p11.C_DecryptInit(session.id(), ckMechanism, p11Key.keyID);
 322                 break;
 323             case MODE_SIGN:
 324                 p11.C_SignInit(session.id(), ckMechanism, p11Key.keyID);
 325                 break;
 326             case MODE_VERIFY:
 327                 p11.C_VerifyRecoverInit(session.id(), ckMechanism, p11Key.keyID);
 328                 break;
 329             default:
 330                 throw new AssertionError("internal error");
 331             }
 332         } catch (Throwable t) {
 333             p11Key.decNativeKeyRef();
 334             session = token.releaseSession(session);
 335             throw t;
 336         }
 337         initialized = true;
 338         bufOfs = 0;
 339     }
 340 
 341     private void implUpdate(byte[] in, int inOfs, int inLen) {
 342         try {
 343             ensureInitialized();
 344         } catch (PKCS11Exception e) {
 345             throw new ProviderException("update() failed", e);
 346         }
 347         if ((inLen == 0) || (in == null)) {
 348             return;
 349         }
 350         if (bufOfs + inLen > maxInputSize) {
 351             bufOfs = maxInputSize + 1;
 352             return;
 353         }
 354         System.arraycopy(in, inOfs, buffer, bufOfs, inLen);
 355         bufOfs += inLen;
 356     }
 357 
 358     private int implDoFinal(byte[] out, int outOfs, int outLen)
 359             throws BadPaddingException, IllegalBlockSizeException {
 360         if (bufOfs > maxInputSize) {
 361             throw new IllegalBlockSizeException("Data must not be longer "
 362                 + "than " + maxInputSize + " bytes");
 363         }
 364         try {
 365             ensureInitialized();
 366             PKCS11 p11 = token.p11;
 367             int n;
 368             switch (mode) {
 369             case MODE_ENCRYPT:
 370                 n = p11.C_Encrypt
 371                         (session.id(), buffer, 0, bufOfs, out, outOfs, outLen);
 372                 break;
 373             case MODE_DECRYPT:
 374                 n = p11.C_Decrypt
 375                         (session.id(), buffer, 0, bufOfs, out, outOfs, outLen);
 376                 break;
 377             case MODE_SIGN:
 378                 byte[] tmpBuffer = new byte[bufOfs];
 379                 System.arraycopy(buffer, 0, tmpBuffer, 0, bufOfs);
 380                 tmpBuffer = p11.C_Sign(session.id(), tmpBuffer);
 381                 if (tmpBuffer.length > outLen) {
 382                     throw new BadPaddingException(
 383                         "Output buffer (" + outLen + ") is too small to " +
 384                         "hold the produced data (" + tmpBuffer.length + ")");
 385                 }
 386                 System.arraycopy(tmpBuffer, 0, out, outOfs, tmpBuffer.length);
 387                 n = tmpBuffer.length;
 388                 break;
 389             case MODE_VERIFY:
 390                 n = p11.C_VerifyRecover
 391                         (session.id(), buffer, 0, bufOfs, out, outOfs, outLen);
 392                 break;
 393             default:
 394                 throw new ProviderException("internal error");
 395             }
 396             return n;
 397         } catch (PKCS11Exception e) {
 398             throw (BadPaddingException)new BadPaddingException
 399                 ("doFinal() failed").initCause(e);
 400         } finally {
 401             reset(false);
 402         }
 403     }
 404 
 405     // see JCE spec
 406     protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
 407         implUpdate(in, inOfs, inLen);
 408         return B0;
 409     }
 410 
 411     // see JCE spec
 412     protected int engineUpdate(byte[] in, int inOfs, int inLen,
 413             byte[] out, int outOfs) throws ShortBufferException {
 414         implUpdate(in, inOfs, inLen);
 415         return 0;
 416     }
 417 
 418     // see JCE spec
 419     protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
 420             throws IllegalBlockSizeException, BadPaddingException {
 421         implUpdate(in, inOfs, inLen);
 422         int n = implDoFinal(buffer, 0, buffer.length);
 423         byte[] out = new byte[n];
 424         System.arraycopy(buffer, 0, out, 0, n);
 425         return out;
 426     }
 427 
 428     // see JCE spec
 429     protected int engineDoFinal(byte[] in, int inOfs, int inLen,
 430             byte[] out, int outOfs) throws ShortBufferException,
 431             IllegalBlockSizeException, BadPaddingException {
 432         implUpdate(in, inOfs, inLen);
 433         return implDoFinal(out, outOfs, out.length - outOfs);
 434     }
 435 
 436     private byte[] doFinal() throws BadPaddingException,
 437             IllegalBlockSizeException {
 438         byte[] t = new byte[2048];
 439         int n = implDoFinal(t, 0, t.length);
 440         byte[] out = new byte[n];
 441         System.arraycopy(t, 0, out, 0, n);
 442         return out;
 443     }
 444 
 445     // see JCE spec
 446     protected byte[] engineWrap(Key key) throws InvalidKeyException,
 447             IllegalBlockSizeException {
 448         String keyAlg = key.getAlgorithm();
 449         P11Key sKey = null;
 450         try {
 451             // The conversion may fail, e.g. trying to wrap an AES key on
 452             // a token that does not support AES, or when the key size is
 453             // not within the range supported by the token.
 454             sKey = P11SecretKeyFactory.convertKey(token, key, keyAlg);
 455         } catch (InvalidKeyException ike) {
 456             byte[] toBeWrappedKey = key.getEncoded();
 457             if (toBeWrappedKey == null) {
 458                 throw new InvalidKeyException
 459                         ("wrap() failed, no encoding available", ike);
 460             }
 461             // Directly encrypt the key encoding when key conversion failed
 462             implInit(Cipher.ENCRYPT_MODE, p11Key);
 463             implUpdate(toBeWrappedKey, 0, toBeWrappedKey.length);
 464             try {
 465                 return doFinal();
 466             } catch (BadPaddingException bpe) {
 467                 // should not occur
 468                 throw new InvalidKeyException("wrap() failed", bpe);
 469             } finally {
 470                 // Restore original mode
 471                 implInit(Cipher.WRAP_MODE, p11Key);
 472             }
 473         }
 474         Session s = null;
 475         try {
 476             s = token.getOpSession();
 477             p11Key.incNativeKeyRef();
 478             sKey.incNativeKeyRef();
 479             return token.p11.C_WrapKey(s.id(), new CK_MECHANISM(mechanism),
 480                 p11Key.keyID, sKey.keyID);
 481         } catch (PKCS11Exception e) {
 482             throw new InvalidKeyException("wrap() failed", e);
 483         } finally {
 484             p11Key.decNativeKeyRef();
 485             sKey.decNativeKeyRef();
 486             token.releaseSession(s);
 487         }
 488     }
 489 
 490     // see JCE spec
 491     @SuppressWarnings("deprecation")
 492     protected Key engineUnwrap(byte[] wrappedKey, String algorithm,
 493             int type) throws InvalidKeyException, NoSuchAlgorithmException {
 494 
 495         boolean isTlsRsaPremasterSecret =
 496                 algorithm.equals("TlsRsaPremasterSecret");
 497         Exception failover = null;
 498 
 499         // Should C_Unwrap be preferred for non-TLS RSA premaster secret?
 500         if (token.supportsRawSecretKeyImport()) {
 501             // XXX implement unwrap using C_Unwrap() for all keys
 502             implInit(Cipher.DECRYPT_MODE, p11Key);
 503             try {
 504                 if (wrappedKey.length > maxInputSize) {
 505                     throw new InvalidKeyException("Key is too long for unwrapping");
 506                 }
 507 
 508                 byte[] encoded = null;
 509                 implUpdate(wrappedKey, 0, wrappedKey.length);
 510                 try {
 511                     encoded = doFinal();
 512                 } catch (BadPaddingException e) {
 513                     if (isTlsRsaPremasterSecret) {
 514                         failover = e;
 515                     } else {
 516                         throw new InvalidKeyException("Unwrapping failed", e);
 517                     }
 518                 } catch (IllegalBlockSizeException e) {
 519                     // should not occur, handled with length check above
 520                     throw new InvalidKeyException("Unwrapping failed", e);
 521                 }
 522 
 523                 if (isTlsRsaPremasterSecret) {
 524                     if (!(spec instanceof TlsRsaPremasterSecretParameterSpec)) {
 525                         throw new IllegalStateException(
 526                                 "No TlsRsaPremasterSecretParameterSpec specified");
 527                     }
 528 
 529                     // polish the TLS premaster secret
 530                     TlsRsaPremasterSecretParameterSpec psps =
 531                             (TlsRsaPremasterSecretParameterSpec)spec;
 532                     encoded = KeyUtil.checkTlsPreMasterSecretKey(
 533                             psps.getClientVersion(), psps.getServerVersion(),
 534                             random, encoded, (failover != null));
 535                 }
 536 
 537                 return ConstructKeys.constructKey(encoded, algorithm, type);
 538             } finally {
 539                 // Restore original mode
 540                 implInit(Cipher.UNWRAP_MODE, p11Key);
 541             }
 542         } else {
 543             Session s = null;
 544             SecretKey secretKey = null;
 545             try {
 546                 try {
 547                     s = token.getObjSession();
 548                     long keyType = CKK_GENERIC_SECRET;
 549                     CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
 550                             new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
 551                             new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType),
 552                         };
 553                     attributes = token.getAttributes(
 554                             O_IMPORT, CKO_SECRET_KEY, keyType, attributes);
 555                     p11Key.incNativeKeyRef();
 556                     long keyID;
 557                     try {
 558                         keyID = token.p11.C_UnwrapKey(s.id(),
 559                                 new CK_MECHANISM(mechanism), p11Key.keyID,
 560                             wrappedKey, attributes);
 561                     } finally {
 562                         p11Key.decNativeKeyRef();
 563                     }
 564                     secretKey = P11Key.secretKey(s, keyID,
 565                             algorithm, 48 << 3, attributes, true);
 566                 } catch (PKCS11Exception e) {
 567                     if (isTlsRsaPremasterSecret) {
 568                         failover = e;
 569                     } else {
 570                         throw new InvalidKeyException("unwrap() failed", e);
 571                     }
 572                 }
 573 
 574                 if (isTlsRsaPremasterSecret) {
 575                     TlsRsaPremasterSecretParameterSpec psps =
 576                             (TlsRsaPremasterSecretParameterSpec)spec;
 577 
 578                     // Please use the tricky failover as the parameter so that
 579                     // smart compiler won't dispose the unused variable.
 580                     secretKey = polishPreMasterSecretKey(token, s,
 581                             failover, secretKey,
 582                             psps.getClientVersion(), psps.getServerVersion());
 583                 }
 584 
 585                 return secretKey;
 586             } finally {
 587                 token.releaseSession(s);
 588             }
 589         }
 590     }
 591 
 592     // see JCE spec
 593     protected int engineGetKeySize(Key key) throws InvalidKeyException {
 594         int n = P11KeyFactory.convertKey(token, key, algorithm).length();
 595         return n;
 596     }
 597 
 598     private static SecretKey polishPreMasterSecretKey(
 599             Token token, Session session,
 600             Exception failover, SecretKey unwrappedKey,
 601             int clientVersion, int serverVersion) {
 602 
 603         SecretKey newKey;
 604         CK_VERSION version = new CK_VERSION(
 605                 (clientVersion >>> 8) & 0xFF, clientVersion & 0xFF);
 606         try {
 607             CK_ATTRIBUTE[] attributes = token.getAttributes(
 608                     O_GENERATE, CKO_SECRET_KEY,
 609                     CKK_GENERIC_SECRET, new CK_ATTRIBUTE[0]);
 610             long keyID = token.p11.C_GenerateKey(session.id(),
 611                     new CK_MECHANISM(CKM_SSL3_PRE_MASTER_KEY_GEN, version),
 612                     attributes);
 613             newKey = P11Key.secretKey(session,
 614                     keyID, "TlsRsaPremasterSecret", 48 << 3, attributes, true);
 615         } catch (PKCS11Exception e) {
 616             throw new ProviderException(
 617                     "Could not generate premaster secret", e);
 618         }
 619 
 620         return (failover == null) ? unwrappedKey : newKey;
 621     }
 622 
 623 }
 624 
 625 final class ConstructKeys {
 626     /**
 627      * Construct a public key from its encoding.
 628      *
 629      * @param encodedKey the encoding of a public key.
 630      *
 631      * @param encodedKeyAlgorithm the algorithm the encodedKey is for.
 632      *
 633      * @return a public key constructed from the encodedKey.
 634      */
 635     private static final PublicKey constructPublicKey(byte[] encodedKey,
 636             String encodedKeyAlgorithm)
 637             throws InvalidKeyException, NoSuchAlgorithmException {
 638         try {
 639             KeyFactory keyFactory =
 640                 KeyFactory.getInstance(encodedKeyAlgorithm);
 641             X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey);
 642             return keyFactory.generatePublic(keySpec);
 643         } catch (NoSuchAlgorithmException nsae) {
 644             throw new NoSuchAlgorithmException("No installed providers " +
 645                                                "can create keys for the " +
 646                                                encodedKeyAlgorithm +
 647                                                "algorithm", nsae);
 648         } catch (InvalidKeySpecException ike) {
 649             throw new InvalidKeyException("Cannot construct public key", ike);
 650         }
 651     }
 652 
 653     /**
 654      * Construct a private key from its encoding.
 655      *
 656      * @param encodedKey the encoding of a private key.
 657      *
 658      * @param encodedKeyAlgorithm the algorithm the wrapped key is for.
 659      *
 660      * @return a private key constructed from the encodedKey.
 661      */
 662     private static final PrivateKey constructPrivateKey(byte[] encodedKey,
 663             String encodedKeyAlgorithm) throws InvalidKeyException,
 664             NoSuchAlgorithmException {
 665         try {
 666             KeyFactory keyFactory =
 667                 KeyFactory.getInstance(encodedKeyAlgorithm);
 668             PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
 669             return keyFactory.generatePrivate(keySpec);
 670         } catch (NoSuchAlgorithmException nsae) {
 671             throw new NoSuchAlgorithmException("No installed providers " +
 672                                                "can create keys for the " +
 673                                                encodedKeyAlgorithm +
 674                                                "algorithm", nsae);
 675         } catch (InvalidKeySpecException ike) {
 676             throw new InvalidKeyException("Cannot construct private key", ike);
 677         }
 678     }
 679 
 680     /**
 681      * Construct a secret key from its encoding.
 682      *
 683      * @param encodedKey the encoding of a secret key.
 684      *
 685      * @param encodedKeyAlgorithm the algorithm the secret key is for.
 686      *
 687      * @return a secret key constructed from the encodedKey.
 688      */
 689     private static final SecretKey constructSecretKey(byte[] encodedKey,
 690             String encodedKeyAlgorithm) {
 691         return new SecretKeySpec(encodedKey, encodedKeyAlgorithm);
 692     }
 693 
 694     static final Key constructKey(byte[] encoding, String keyAlgorithm,
 695             int keyType) throws InvalidKeyException, NoSuchAlgorithmException {
 696         switch (keyType) {
 697         case Cipher.SECRET_KEY:
 698             return constructSecretKey(encoding, keyAlgorithm);
 699         case Cipher.PRIVATE_KEY:
 700             return constructPrivateKey(encoding, keyAlgorithm);
 701         case Cipher.PUBLIC_KEY:
 702             return constructPublicKey(encoding, keyAlgorithm);
 703         default:
 704             throw new InvalidKeyException("Unknown keytype " + keyType);
 705         }
 706     }
 707 }