1 /*
   2  * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.crypto.provider;
  27 
  28 import java.io.IOException;
  29 import java.security.AlgorithmParameters;
  30 import java.security.AlgorithmParametersSpi;
  31 import java.security.NoSuchAlgorithmException;
  32 import java.security.NoSuchProviderException;
  33 import java.security.spec.AlgorithmParameterSpec;
  34 import java.security.spec.InvalidParameterSpecException;
  35 import javax.crypto.spec.IvParameterSpec;
  36 import javax.crypto.spec.PBEParameterSpec;
  37 import javax.crypto.spec.RC2ParameterSpec;
  38 import javax.crypto.spec.RC5ParameterSpec;
  39 import sun.security.util.DerOutputStream;
  40 import sun.security.util.DerValue;
  41 import sun.security.util.ObjectIdentifier;
  42 import sun.security.x509.AlgorithmId;
  43 
  44 /**
  45  * This class implements the parameter set used with password-based
  46  * encryption scheme 2 (PBES2), which is defined in PKCS#5 as follows:
  47  *
  48  * <pre>
  49  * -- PBES2
  50  *
  51  * PBES2Algorithms ALGORITHM-IDENTIFIER ::=
  52  *   { {PBES2-params IDENTIFIED BY id-PBES2}, ...}
  53  *
  54  * id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13}
  55  *
  56  * PBES2-params ::= SEQUENCE {
  57  *   keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
  58  *   encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} }
  59  *
  60  * PBES2-KDFs ALGORITHM-IDENTIFIER ::=
  61  *   { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... }
  62  *
  63  * PBES2-Encs ALGORITHM-IDENTIFIER ::= { ... }
  64  *
  65  * -- PBKDF2
  66  *
  67  * PBKDF2Algorithms ALGORITHM-IDENTIFIER ::=
  68  *   { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ...}
  69  *
  70  * id-PBKDF2 OBJECT IDENTIFIER ::= {pkcs-5 12}
  71  *
  72  * PBKDF2-params ::= SEQUENCE {
  73  *     salt CHOICE {
  74  *       specified OCTET STRING,
  75  *       otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
  76  *     },
  77  *     iterationCount INTEGER (1..MAX),
  78  *     keyLength INTEGER (1..MAX) OPTIONAL,
  79  *     prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1
  80  * }
  81  *
  82  * PBKDF2-SaltSources ALGORITHM-IDENTIFIER ::= { ... }
  83  *
  84  * PBKDF2-PRFs ALGORITHM-IDENTIFIER ::= {
  85  *     {NULL IDENTIFIED BY id-hmacWithSHA1} |
  86  *     {NULL IDENTIFIED BY id-hmacWithSHA224} |
  87  *     {NULL IDENTIFIED BY id-hmacWithSHA256} |
  88  *     {NULL IDENTIFIED BY id-hmacWithSHA384} |
  89  *     {NULL IDENTIFIED BY id-hmacWithSHA512}, ... }
  90  *
  91  * algid-hmacWithSHA1 AlgorithmIdentifier {{PBKDF2-PRFs}} ::=
  92  *     {algorithm id-hmacWithSHA1, parameters NULL : NULL}
  93  *
  94  * id-hmacWithSHA1 OBJECT IDENTIFIER ::= {digestAlgorithm 7}
  95  *
  96  * PBES2-Encs ALGORITHM-IDENTIFIER ::= { ... }
  97  *
  98  * </pre>
  99  */
 100 abstract class PBES2Parameters extends AlgorithmParametersSpi {
 101 
 102     // PKCS#5/8 AlgorithmIdentifier OIDs
 103     private static final ObjectIdentifier pkcs5PBKDF2_OID =
 104             ObjectIdentifier.of("1.2.840.113549.1.5.12");
 105     private static final ObjectIdentifier pkcs5PBES2_OID =
 106             ObjectIdentifier.of("1.2.840.113549.1.5.13");
 107 
 108     // PBKDF2 PRF OIDs
 109     private static final ObjectIdentifier hmacWithSHA1_OID =
 110             ObjectIdentifier.of("1.2.840.113549.2.7");
 111     private static final ObjectIdentifier hmacWithSHA224_OID =
 112             ObjectIdentifier.of("1.2.840.113549.2.8");
 113     private static final ObjectIdentifier hmacWithSHA256_OID =
 114             ObjectIdentifier.of("1.2.840.113549.2.9");
 115     private static final ObjectIdentifier hmacWithSHA384_OID =
 116             ObjectIdentifier.of("1.2.840.113549.2.10");
 117     private static final ObjectIdentifier hmacWithSHA512_OID =
 118             ObjectIdentifier.of("1.2.840.113549.2.11");
 119     private static final ObjectIdentifier hmacWithSHA512_224_OID =
 120             ObjectIdentifier.of("1.2.840.113549.2.12");
 121     private static final ObjectIdentifier hmacWithSHA512_256_OID =
 122             ObjectIdentifier.of("1.2.840.113549.2.13");
 123 
 124     // Encryption Scheme OIDs
 125     private static final ObjectIdentifier aes128CBC_OID =
 126             ObjectIdentifier.of("2.16.840.1.101.3.4.1.2");
 127     private static final ObjectIdentifier aes192CBC_OID =
 128             ObjectIdentifier.of("2.16.840.1.101.3.4.1.22");
 129     private static final ObjectIdentifier aes256CBC_OID =
 130             ObjectIdentifier.of("2.16.840.1.101.3.4.1.42");
 131     private static final ObjectIdentifier desCBC_OID =
 132             ObjectIdentifier.of("1.3.14.3.2.7");
 133     private static final ObjectIdentifier desEDE3CBC_OID =
 134             ObjectIdentifier.of("1.2.840.113549.3.7");
 135     private static final ObjectIdentifier rc2CBC_OID =
 136             ObjectIdentifier.of("1.2.840.113549.3.2");
 137     private static final ObjectIdentifier rc5CBC_OID =
 138             ObjectIdentifier.of("1.2.840.113549.3.9");
 139 
 140     // Other PRF and Encryption Scheme constants
 141     private static final int AES_IVLEN = 16;
 142     private static final int DES_IVLEN = 8;
 143     private static final int RC2_IVLEN = 8;
 144     private static final int RC5_IVLEN_64 = 8;
 145     private static final int RC5_IVLEN_128 = 16;
 146 
 147     // Used to indicate both variable length or an unspecified length
 148     private static final int KEYLEN_UNSPEC = -1;
 149 
 150     // the PBES2 algorithm name
 151     private String pbes2AlgorithmName = "PBES2";
 152 
 153     // the salt
 154     private byte[] salt = null;
 155 
 156     // the iteration count
 157     private int iCount = 0;
 158 
 159     // the cipher parameter
 160     private AlgorithmParameterSpec cipherParam = null;
 161 
 162     // the key derivation function (default is HmacSHA1)
 163     private PrfType kdfType = null;
 164 
 165     // the encryption function
 166     private EncType encryptionType = null;
 167 
 168     // the cipher keysize (in bits)
 169     private int keysize = KEYLEN_UNSPEC;
 170 
 171     /**
 172      * Define the basic characteristics of the PRFs that we are able to
 173      * process.
 174      */
 175     enum PrfType {
 176         HMAC_SHA1(hmacWithSHA1_OID, "HmacSHA1"),
 177         HMAC_SHA224(hmacWithSHA224_OID, "HmacSHA224"),
 178         HMAC_SHA256(hmacWithSHA256_OID, "HmacSHA256"),
 179         HMAC_SHA384(hmacWithSHA384_OID, "HmacSHA384"),
 180         HMAC_SHA512(hmacWithSHA512_OID, "HmacSHA512"),
 181         HMAC_SHA512_224(hmacWithSHA512_224_OID, "HmacSHA512/224"),
 182         HMAC_SHA512_256(hmacWithSHA512_256_OID, "HmacSHA512/256");
 183 
 184         final ObjectIdentifier schemeOid;
 185         final String schemeName;
 186 
 187         private PrfType(ObjectIdentifier oid, String cName) {
 188             this.schemeOid = oid;
 189             this.schemeName = cName;
 190         }
 191 
 192         static PrfType getByName(String name) {
 193             for (PrfType prf : PrfType.values()) {
 194                 if (prf.schemeName.equalsIgnoreCase(name)) {
 195                     return prf;
 196                 }
 197             }
 198             return null;
 199         }
 200 
 201         static PrfType getByOid(ObjectIdentifier oid) {
 202             for (PrfType prf : PrfType.values()) {
 203                 if (prf.schemeOid.equals(oid)) {
 204                     return prf;
 205                 }
 206             }
 207             return null;
 208         }
 209     }
 210 
 211     enum EncType {
 212         AES_128_CBC("AES", true, 128, aes128CBC_OID, IvParameterSpec.class),
 213         AES_192_CBC("AES", true, 192, aes192CBC_OID, IvParameterSpec.class),
 214         AES_256_CBC("AES", true, 256, aes256CBC_OID, IvParameterSpec.class),
 215         DES_CBC("DES", true, 64, desCBC_OID, IvParameterSpec.class),
 216         DES_EDE3_CBC("DESede", true, 192, desEDE3CBC_OID,
 217                     IvParameterSpec.class),
 218         RC2_CBC("RC2", false, KEYLEN_UNSPEC, rc2CBC_OID, RC2ParameterSpec.class),
 219         RC5_CBC("RC5", false, KEYLEN_UNSPEC, rc5CBC_OID, RC5ParameterSpec.class);
 220 
 221         final String schemeName;
 222         final boolean fixedKeySize;
 223         final int keyLen;
 224         final ObjectIdentifier oid;
 225         final Class<? extends AlgorithmParameterSpec> specClass;
 226 
 227         private EncType(String name, boolean fixed, int keyLen,
 228                 ObjectIdentifier oid,
 229                 Class<? extends AlgorithmParameterSpec> clazz) {
 230             schemeName = name;
 231             fixedKeySize = fixed;
 232             this.keyLen = keyLen;
 233             this.oid = oid;
 234             specClass = clazz;
 235         }
 236 
 237         static EncType getByOid(ObjectIdentifier oid) {
 238             for (EncType enc : EncType.values()) {
 239                 if (enc.oid.equals(oid)) {
 240                     return enc;
 241                 }
 242             }
 243             return null;
 244         }
 245 
 246         static EncType getByName(String algName, int keylen) {
 247             for (EncType enc : EncType.values()) {
 248                 if (enc.schemeName.equalsIgnoreCase(algName)) {
 249                     if (enc.fixedKeySize) {
 250                         if (enc.keyLen == keylen ||
 251                                 enc.keyLen == KEYLEN_UNSPEC) {
 252                             // This case is for schemes where the key size
 253                             // is fixed either by OID (e.g. AES-128/192/256)\
 254                             // or where it is always the same for an algorithm
 255                             // and therefore not specified by name (DES/DESede).
 256                             return enc;
 257                         }
 258                     } else {
 259                         // For a variable length key a single enc scheme
 260                         // will handle all values and they will be verified
 261                         // later if specified in DER.
 262                         return enc;
 263                     }
 264                 }
 265             }
 266             return null;
 267         }
 268     }
 269 
 270     protected PBES2Parameters() {
 271         // KDF, encryption & keysize values are set later, in engineInit(byte[])
 272     }
 273 
 274     protected PBES2Parameters(String pbes2AlgorithmName)
 275             throws NoSuchAlgorithmException {
 276         int and;
 277         String kdfAlgo = null;
 278         String cipherAlgo = null;
 279 
 280         // Extract the KDF and encryption algorithm names
 281         this.pbes2AlgorithmName = pbes2AlgorithmName;
 282         if (pbes2AlgorithmName.startsWith("PBEWith") &&
 283             (and = pbes2AlgorithmName.indexOf("And", 7 + 1)) > 0) {
 284             kdfAlgo = pbes2AlgorithmName.substring(7, and);
 285             cipherAlgo = pbes2AlgorithmName.substring(and + 3);
 286 
 287             // Check for keysize
 288             int underscore;
 289             if ((underscore = cipherAlgo.indexOf('_')) > 0) {
 290                 int slash;
 291                 if ((slash = cipherAlgo.indexOf('/', underscore + 1)) > 0) {
 292                     keysize =
 293                         Integer.parseInt(cipherAlgo.substring(underscore + 1,
 294                             slash));
 295                 } else {
 296                     keysize =
 297                         Integer.parseInt(cipherAlgo.substring(underscore + 1));
 298                 }
 299                 cipherAlgo = cipherAlgo.substring(0, underscore);
 300             }
 301         } else {
 302             throw new NoSuchAlgorithmException("No crypto implementation for " +
 303                 pbes2AlgorithmName);
 304         }
 305 
 306         kdfType = PrfType.getByName(kdfAlgo);
 307         if (kdfType == null) {
 308             throw new NoSuchAlgorithmException("Unsupported KDF: " + kdfAlgo);
 309         }
 310         encryptionType = EncType.getByName(cipherAlgo, keysize);
 311         if (encryptionType == null) {
 312             throw new NoSuchAlgorithmException(
 313                     "Unsupported Encryption Scheme: " + cipherAlgo);
 314         }
 315     }
 316 
 317     @Override
 318     protected void engineInit(AlgorithmParameterSpec paramSpec)
 319             throws InvalidParameterSpecException {
 320         if (!(paramSpec instanceof PBEParameterSpec)) {
 321             throw new InvalidParameterSpecException
 322                 ("Inappropriate parameter specification");
 323         }
 324         this.salt = ((PBEParameterSpec)paramSpec).getSalt().clone();
 325         this.iCount = ((PBEParameterSpec)paramSpec).getIterationCount();
 326         this.cipherParam = ((PBEParameterSpec)paramSpec).getParameterSpec();
 327 
 328         // In this form of initialization, if the kdfType has not
 329         // been set by the constructor, we will set it to the default
 330         // HMAC-SHA1.
 331         if (kdfType == null) {
 332             kdfType = PrfType.HMAC_SHA1;
 333         }
 334 
 335         // Perform a sanity check to make sure that the supplied
 336         // cipher AlgorithmParameterSpec is the correct type for the selected
 337         // encryption scheme that was chosen at instantiation time.
 338         if (encryptionType != null) {
 339             validateEncParams();
 340             try {
 341                 pbes2AlgorithmName = String.format("PBEWith%sAnd%s",
 342                     kdfType.schemeName, getEncSchemeName());
 343             } catch (NoSuchAlgorithmException nsae) {
 344                 throw (InvalidParameterSpecException)
 345                         new InvalidParameterSpecException().initCause(nsae);
 346             }
 347         } else {
 348             // This branch will be walked if the generic form of the
 349             // PBES2Parameters is created.  Use of this form of init with
 350             // PBES2Parameters.General is disallowed.  This is because in some
 351             // cases there is no way to know from the submitted cipher
 352             // AlgorithmParameterSpec which encryption scheme has been
 353             // selected (i.e. multiple CBC-based ciphers all use
 354             // IvParameterSpec)
 355             throw new InvalidParameterSpecException("Invalid initialization " +
 356                     "method when no encryption scheme explicitly selected.");
 357         }
 358     }
 359 
 360     @Override
 361     protected void engineInit(byte[] encoded, String decodingMethod)
 362         throws IOException {
 363         engineInit(encoded);
 364     }
 365 
 366     @Override
 367     protected void engineInit(byte[] encoded) throws IOException {
 368         DerValue pBES2_params = new DerValue(encoded);
 369         if (pBES2_params.tag != DerValue.tag_Sequence) {
 370             throw new IOException("PBE parameter parsing error: "
 371                 + "not an ASN.1 SEQUENCE tag");
 372         }
 373         DerValue kdf = pBES2_params.data.getDerValue();
 374 
 375         // Before JDK-8202837, PBES2-params was mistakenly encoded like
 376         // an AlgorithmId which is a sequence of its own OID and the real
 377         // PBES2-params. If the first DerValue is an OID instead of a
 378         // PBES2-KDFs (which should be a SEQUENCE), we are likely to be
 379         // dealing with this buggy encoding. Skip the OID and treat the
 380         // next DerValue as the real PBES2-params.
 381         if (kdf.getTag() == DerValue.tag_ObjectId) {
 382             pBES2_params = pBES2_params.data.getDerValue();
 383             kdf = pBES2_params.data.getDerValue();
 384         }
 385 
 386         parseKDF(kdf);
 387 
 388         parseES(pBES2_params.data.getDerValue());
 389 
 390         try {
 391             validateEncParams();
 392             pbes2AlgorithmName = String.format("PBEWith%sAnd%s",
 393                 kdfType.schemeName, getEncSchemeName());
 394         } catch (InvalidParameterSpecException | NoSuchAlgorithmException exc) {
 395             throw new IOException(exc);
 396         }
 397     }
 398 
 399     /**
 400      * Parse the PBES2-KDFs portion of the PBES2 parameters structure:
 401      *
 402      * PBES2-KDFs ALGORITHM-IDENTIFIER ::=
 403      *      { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... }
 404      *
 405      * PBKDF2-params ::= SEQUENCE {
 406      *      salt CHOICE {
 407      *          specified OCTET STRING,
 408      *          otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
 409      *      },
 410      *      iterationCount INTEGER (1..MAX),
 411      *      keyLength INTEGER (1..MAX) OPTIONAL,
 412      *      prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1
 413      * }
 414      *
 415      * @param keyDerivationFunc a DerValue built from the outer SEQUENCE
 416      *      that begins the PBES2-KDFs structure.
 417      *
 418      * @throws IOException if any processing failures occur or the values
 419      *      read sit outside acceptable ranges.
 420      */
 421     private void parseKDF(DerValue keyDerivationFunc) throws IOException {
 422         // The incoming keyDerivationFunc is an AlgorithmID.  Ensure that
 423         // the DerValue is a SEQUENCE with the first element being the correct
 424         // PBKDF2 OID.
 425         if (keyDerivationFunc.tag != DerValue.tag_Sequence) {
 426             throw new IOException("PBES2-KDFs DerValue is not a SEQUENCE");
 427         } else if (!pkcs5PBKDF2_OID.equals(keyDerivationFunc.data.getOID())) {
 428             throw new IOException("PBE parameter parsing error: "
 429                 + "expecting the object identifier for PBKDF2");
 430         }
 431 
 432         // Pull out all the elements out of the PBKDF2-params structure
 433         DerValue[] pk2Params = keyDerivationFunc.data.getSequence(4);
 434         int idx = 0;
 435         if (pk2Params.length < 2 || pk2Params.length > 4) {
 436             throw new IOException("Invalid number of PBKDF2-params elements " +
 437                     "(2 <= X <= 4), got " + pk2Params.length);
 438         }
 439 
 440         DerValue curParam = pk2Params[idx++];
 441         // salt: specified OCTET STRING
 442         // the 'otherSource' ASN.1 CHOICE for 'salt' is not supported
 443         salt = curParam.getOctetString();
 444 
 445         // iterationCount INTEGER (1..MAX)
 446         iCount = pk2Params[idx++].getInteger();
 447         if (iCount < 1) {
 448             throw new IOException("Illegal non-positive iteration count: " +
 449                     iCount);
 450         }
 451 
 452         // keyLength INTEGER (1..MAX) OPTIONAL
 453         if (idx < pk2Params.length) {
 454             curParam = pk2Params[idx];
 455             if (curParam.tag == DerValue.tag_Integer) {
 456                 int keysizeBytes = curParam.getInteger();
 457                 if (keysizeBytes < 1) {
 458                     throw new IOException("Illegal non-positive key length: " +
 459                             keysizeBytes);
 460                 } else {
 461                     // If keyLength is specified, it should be consistent
 462                     // with the declared key length (if any).
 463                     int derKeyBits = keysizeBytes * 8;
 464                     if (keysize >= 0) {
 465                         if (keysize != derKeyBits) {
 466                             throw new IOException("PBKDF2-param keyLength " +
 467                                     "mismatch: expected " + keysize +
 468                                     ", received " + derKeyBits);
 469                         }
 470                     } else {
 471                         keysize = derKeyBits;
 472                     }
 473                     idx++;
 474                 }
 475             }
 476         }
 477 
 478         // prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1
 479         PrfType receivedKdf;
 480         if (idx < pk2Params.length) {
 481             curParam = pk2Params[idx];
 482             // The AlgorithmIdentifier will have an OID and optionally
 483             // parameters.  Currently the parameters for all the PRFs we
 484             // support are either ASN.1 NULL or are omitted entirely.
 485             ObjectIdentifier providedKdfOid = curParam.data.getOID();
 486             receivedKdf = PrfType.getByOid(providedKdfOid);
 487             if (receivedKdf != null) {
 488                 // AlgorithmIdentifiers have the parameters section defined
 489                 // in RFC 5280 as ANY DEFINED BY algorithm OPTIONAL.  Since
 490                 // all the current KDF algs we use are HMAC-SHA* they have
 491                 // no parameters.  We will accept an ASN.1 NULL or a complete
 492                 // omission of the AlgId parameters segment.
 493                 if (curParam.data.available() > 0) {
 494                     curParam.data.getNull();
 495                 }
 496             } else {
 497                 throw new IOException("PBE parameter parsing error: " +
 498                         "Unsupported PRF OID: " + providedKdfOid);
 499             }
 500         } else {
 501             receivedKdf = PrfType.HMAC_SHA1;
 502         }
 503 
 504         // Lastly, if a KDF type has already been set as a result of
 505         // instantiation by the complete PBES2 name
 506         // (e.g. PBEWith<prf>And<encryption>), and the KDF
 507         // OID indicated by the DER encoding does not match, then this is
 508         // an error.
 509         if (kdfType != null && kdfType != receivedKdf) {
 510             throw new IOException("Requested instance KDF does not match " +
 511                     "received KDF type, inst: " + kdfType.schemeOid +
 512                     ", received: " + receivedKdf.schemeOid);
 513         } else {
 514             // Instantiated via the PBES2Parameters.General, the DER determines
 515             // the KDF OID.
 516             kdfType = receivedKdf;
 517         }
 518     }
 519 
 520     /**
 521      * Parse the PBES2-Encs portion of the PBES2 parameters structure:
 522      *
 523      * PBES2-Encs ALGORITHM-IDENTIFIER ::= { ... }
 524      *
 525      * where the definitions of the encryption scheme parameters are found
 526      * in the AlgIdParams implementations for each encryption scheme.
 527      *
 528      * @param encryptionScheme a DerValue built from the outer SEQUENCE
 529      *      that begins the PBES2-Encss structure.
 530      *
 531      * @throws IOException if any processing failures occur or the values
 532      *      read sit outside acceptable ranges.
 533      */
 534     private void parseES(DerValue encryptionScheme) throws IOException {
 535         AlgorithmId encAlgId = AlgorithmId.parse(encryptionScheme);
 536         ObjectIdentifier esOid = encAlgId.getOID();
 537         EncType receivedEncType = EncType.getByOid(esOid);
 538         if (receivedEncType == null) {
 539             throw new IOException("Unsupported encryption scheme for OID " +
 540                     esOid);
 541         }
 542 
 543         // Compare any pre-set encryptionType against what we found in the DER.
 544         if (encryptionType != null && encryptionType != receivedEncType) {
 545             throw new IOException("Requested instance encryption scheme does " +
 546                     "not match the received encryption scheme type.  " +
 547                     "Inst: " + encryptionType.oid +
 548                     ", received: " + receivedEncType.oid);
 549         } else {
 550             // Instantiated via the PBES2Parameters.General, the DER
 551             // determines the EncType.
 552             encryptionType = receivedEncType;
 553         }
 554 
 555         // Pull the data out as an AlgorithmParameterSpec and assign it
 556         // to the main PBES2 object
 557         AlgorithmParameters encrParams = encAlgId.getParameters();
 558         try {
 559             cipherParam = (encrParams != null) ?
 560                     encrParams.getParameterSpec(encryptionType.specClass) :
 561                     null;
 562         } catch (InvalidParameterSpecException ipse) {
 563             throw new IOException(ipse);
 564         }
 565     }
 566 
 567     @Override
 568     protected <T extends AlgorithmParameterSpec>
 569         T engineGetParameterSpec(Class<T> paramSpec)
 570                 throws InvalidParameterSpecException {
 571         if (PBEParameterSpec.class.isAssignableFrom(paramSpec)) {
 572             return paramSpec.cast(new PBEParameterSpec(this.salt,
 573                     this.iCount, this.cipherParam));
 574         } else {
 575             throw new InvalidParameterSpecException
 576                 ("Inappropriate parameter specification");
 577         }
 578     }
 579 
 580     @Override
 581     protected byte[] engineGetEncoded() throws IOException {
 582         DerOutputStream out = new DerOutputStream();
 583 
 584         DerOutputStream pBES2_params = new DerOutputStream();
 585 
 586         DerOutputStream keyDerivationFunc = new DerOutputStream();
 587         keyDerivationFunc.putOID(pkcs5PBKDF2_OID);
 588 
 589         DerOutputStream pBKDF2_params = new DerOutputStream();
 590         pBKDF2_params.putOctetString(salt); // choice: 'specified OCTET STRING'
 591         pBKDF2_params.putInteger(iCount);
 592 
 593         // derived key length (in octets)
 594         // keyLength INTEGER (1..MAX) OPTIONAL
 595         // This only needs to be asserted for OIDs that have variable
 596         // key lengths associated with it.
 597         if (!encryptionType.fixedKeySize && keysize != KEYLEN_UNSPEC) {
 598             pBKDF2_params.putInteger(keysize / 8);
 599         }
 600 
 601         DerOutputStream prf = new DerOutputStream();
 602         prf.putOID(kdfType.schemeOid);
 603         // Note: we do not use an ASN.1 NULL tag for the parameters for any
 604         // of the HMAC-SHA* KDF algs we use.
 605         pBKDF2_params.write(DerValue.tag_Sequence, prf);
 606 
 607         keyDerivationFunc.write(DerValue.tag_Sequence, pBKDF2_params);
 608         pBES2_params.write(DerValue.tag_Sequence, keyDerivationFunc);
 609 
 610         DerOutputStream encryptionScheme = new DerOutputStream();
 611         encryptionScheme.putOID(encryptionType.oid);
 612         if (cipherParam != null) {
 613             try {
 614                 AlgorithmParameters encParams = AlgorithmParameters.getInstance(
 615                         encryptionType.schemeName, "SunJCE");
 616                 encParams.init(cipherParam);
 617                 byte[] encrData = encParams.getEncoded();
 618                 encryptionScheme.write(encrData);
 619             } catch (NoSuchAlgorithmException | NoSuchProviderException |
 620                     InvalidParameterSpecException exc) {
 621                 throw new IOException(exc);
 622             }
 623         } else {
 624             throw new IOException("Missing encryption scheme parameters: " +
 625                     "expected " + encryptionType.specClass.getName());
 626         }
 627         pBES2_params.write(DerValue.tag_Sequence, encryptionScheme);
 628 
 629         out.write(DerValue.tag_Sequence, pBES2_params);
 630 
 631         return out.toByteArray();
 632     }
 633 
 634     @Override
 635     protected byte[] engineGetEncoded(String encodingMethod)
 636         throws IOException {
 637         return engineGetEncoded();
 638     }
 639 
 640     private void validateEncParams() throws InvalidParameterSpecException {
 641         if (this.cipherParam == null) {
 642             // No encryption scheme AlgorithmParameterSpec was provided
 643             // at init time.
 644             return;
 645         }
 646 
 647         if (encryptionType.specClass != null) {
 648             if (!encryptionType.specClass.isAssignableFrom(
 649                     this.cipherParam.getClass())) {
 650                 throw new InvalidParameterSpecException(
 651                         "Illegal parameter spec for encryption OID " +
 652                             encryptionType.oid + ".  Submitted " +
 653                             this.cipherParam.getClass().getName() +
 654                             ", expected " + encryptionType.specClass.getName() +
 655                             " (or subclass)");
 656             }
 657         }
 658 
 659         switch (encryptionType) {
 660             case AES_128_CBC:
 661             case AES_192_CBC:
 662             case AES_256_CBC:
 663                 validateIvPS(AES_IVLEN);
 664                 break;
 665             case DES_CBC:
 666             case DES_EDE3_CBC:
 667                 validateIvPS(DES_IVLEN);
 668                 break;
 669             case RC2_CBC:
 670                 validateRC2PS();
 671                 break;
 672             case RC5_CBC:
 673                 validateRC5PS();
 674                 break;
 675             default:
 676                 throw new InvalidParameterSpecException(
 677                         "Unsupported encryption type");
 678         }
 679 
 680         // Finally perform any key size consistency checks.
 681         // If fixed, the key size should only be checked if it had ever been
 682         // asserted.  If variable, the key size must have been defined
 683         // somehow prior to this, either by name, by encoding, or a default.
 684         if (encryptionType.fixedKeySize) {
 685             if (keysize != KEYLEN_UNSPEC && keysize != encryptionType.keyLen) {
 686                 throw new InvalidParameterSpecException("Key size mismatch. " +
 687                         "Expected: " + encryptionType.keyLen + ", actual: " +
 688                         keysize);
 689             }
 690         } else {
 691             if (keysize == KEYLEN_UNSPEC) {
 692                 throw new InvalidParameterSpecException("Variable length " +
 693                         "encryption scheme has no key size defined");
 694             }
 695         }
 696     }
 697 
 698     private void validateIvPS(int ivReqLen)
 699             throws InvalidParameterSpecException {
 700         IvParameterSpec ivSpec = (IvParameterSpec)cipherParam;
 701         byte[] ivBytes = ivSpec.getIV();
 702         if (ivBytes.length !=  ivReqLen) {
 703             throw new InvalidParameterSpecException(
 704                     "Incorrect IV length, expected " + ivReqLen +
 705                     " bytes, received " + ivBytes.length);
 706         }
 707     }
 708 
 709     private void validateRC2PS() throws InvalidParameterSpecException {
 710         RC2ParameterSpec rc2Spec = (RC2ParameterSpec)cipherParam;
 711 
 712         // If an IV has been set, make sure it is of the correct length
 713         byte[] ivBytes = rc2Spec.getIV();
 714         if (ivBytes.length != RC2_IVLEN) {
 715             throw new InvalidParameterSpecException(
 716                     "Incorrect IV length, expected " + RC2_IVLEN +
 717                     " bytes, received " + ivBytes.length);
 718         }
 719 
 720         // It is possible that key length is still unset if initialized by
 721         // DER encoding with no key length called out in the PBKDF2-Params.
 722         // If the key length is unset, then set it based on the effective
 723         // key bits from this AlgorithmParameterSpec.  If already set either
 724         // by virtue of the AlgorithmParameters name or from the PBKDF2-Params
 725         // then this value should be consistent with it.
 726         int psKeyLen = rc2Spec.getEffectiveKeyBits();
 727         if (keysize <= 0) {
 728             keysize = psKeyLen;
 729         } else {
 730             if (keysize != psKeyLen) {
 731                 throw new InvalidParameterSpecException("Effective key length" +
 732                         " mismatch: expected " + keysize + ", received: " +
 733                         psKeyLen);
 734             }
 735         }
 736     }
 737 
 738     private void validateRC5PS() throws InvalidParameterSpecException {
 739         // Some of these checks will be redundant if the AlgorithmParameterSpec
 740         // is constructed via DER decoding, but if provided via the engineInit
 741         // method it may have values inconsistent with the ranges in the
 742         // ASN.1 from RFC 8018.
 743         RC5ParameterSpec rc5Spec = (RC5ParameterSpec)cipherParam;
 744         int rounds = rc5Spec.getRounds();
 745         if (rounds < 8 || rounds > 127) {
 746             throw new InvalidParameterSpecException("Rounds must be in the " +
 747                     "range 8..127, received " + rounds);
 748         }
 749 
 750         int wordSize = rc5Spec.getWordSize();
 751         if (wordSize != 32 && wordSize != 64) {
 752             throw new InvalidParameterSpecException("Word size must be " +
 753                     "either 32 or 64 bits, received " + wordSize);
 754         }
 755 
 756         // If we get to this point and for some reason the RC5 PBES2 block
 757         // hasn't specified a key length either in the PBKDF2 block or via
 758         // the name during construction, assign it a default of 128-bit.
 759         if (keysize == KEYLEN_UNSPEC) {
 760             keysize = 128;
 761         }
 762 
 763         // No need to check word size against IV length, as RC5ParameterSpec
 764         // already performs this check upon construction when an IV is provided.
 765     }
 766 
 767     private String getEncSchemeName() throws NoSuchAlgorithmException {
 768         switch (encryptionType) {
 769             case AES_128_CBC:
 770             case AES_192_CBC:
 771             case AES_256_CBC:
 772                 return (encryptionType.schemeName + "_" +
 773                         encryptionType.keyLen);
 774             case DES_CBC:
 775             case DES_EDE3_CBC:
 776                 return encryptionType.schemeName;
 777             case RC2_CBC:
 778             case RC5_CBC:
 779                 return (encryptionType.schemeName + "_" + keysize);
 780             default:
 781                 // Should never happen
 782                 throw new NoSuchAlgorithmException(
 783                         "Unsupported encryption type: " +
 784                                 encryptionType.name());
 785         }
 786     }
 787 
 788     /*
 789      * Returns a formatted string describing the parameters.
 790      *
 791      * The algorithn name pattern is: "PBEWith<prf>And<encryption>"
 792      * where <prf> is one of: HmacSHA1, HmacSHA224, HmacSHA256, HmacSHA384,
 793      * or HmacSHA512, and <encryption> is AES with a keysize suffix.
 794      */
 795     @Override
 796     protected String engineToString() {
 797         return pbes2AlgorithmName;
 798     }
 799 
 800     public static final class General extends PBES2Parameters {
 801         public General() throws NoSuchAlgorithmException {
 802             super();
 803         }
 804     }
 805 
 806     public static final class HmacSHA1AndAES_128 extends PBES2Parameters {
 807         public HmacSHA1AndAES_128() throws NoSuchAlgorithmException {
 808             super("PBEWithHmacSHA1AndAES_128");
 809         }
 810     }
 811 
 812     public static final class HmacSHA224AndAES_128 extends PBES2Parameters {
 813         public HmacSHA224AndAES_128() throws NoSuchAlgorithmException {
 814             super("PBEWithHmacSHA224AndAES_128");
 815         }
 816     }
 817 
 818     public static final class HmacSHA256AndAES_128 extends PBES2Parameters {
 819         public HmacSHA256AndAES_128() throws NoSuchAlgorithmException {
 820             super("PBEWithHmacSHA256AndAES_128");
 821         }
 822     }
 823 
 824     public static final class HmacSHA384AndAES_128 extends PBES2Parameters {
 825         public HmacSHA384AndAES_128() throws NoSuchAlgorithmException {
 826             super("PBEWithHmacSHA384AndAES_128");
 827         }
 828     }
 829 
 830     public static final class HmacSHA512AndAES_128 extends PBES2Parameters {
 831         public HmacSHA512AndAES_128() throws NoSuchAlgorithmException {
 832             super("PBEWithHmacSHA512AndAES_128");
 833         }
 834     }
 835 
 836     public static final class HmacSHA1AndAES_256 extends PBES2Parameters {
 837         public HmacSHA1AndAES_256() throws NoSuchAlgorithmException {
 838             super("PBEWithHmacSHA1AndAES_256");
 839         }
 840     }
 841 
 842     public static final class HmacSHA224AndAES_256 extends PBES2Parameters {
 843         public HmacSHA224AndAES_256() throws NoSuchAlgorithmException {
 844             super("PBEWithHmacSHA224AndAES_256");
 845         }
 846     }
 847 
 848     public static final class HmacSHA256AndAES_256 extends PBES2Parameters {
 849         public HmacSHA256AndAES_256() throws NoSuchAlgorithmException {
 850             super("PBEWithHmacSHA256AndAES_256");
 851         }
 852     }
 853 
 854     public static final class HmacSHA384AndAES_256 extends PBES2Parameters {
 855         public HmacSHA384AndAES_256() throws NoSuchAlgorithmException {
 856             super("PBEWithHmacSHA384AndAES_256");
 857         }
 858     }
 859 
 860     public static final class HmacSHA512AndAES_256 extends PBES2Parameters {
 861         public HmacSHA512AndAES_256() throws NoSuchAlgorithmException {
 862             super("PBEWithHmacSHA512AndAES_256");
 863         }
 864     }
 865 }