001/*
002 * Copyright 2022-2023 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2022-2023 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2022-2023 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.util.ssl.cert;
037
038
039
040import java.util.Collections;
041import java.util.HashSet;
042import java.util.Map;
043import java.util.Set;
044
045import com.unboundid.util.NotNull;
046import com.unboundid.util.Nullable;
047import com.unboundid.util.OID;
048import com.unboundid.util.ObjectPair;
049import com.unboundid.util.StaticUtils;
050import com.unboundid.util.ThreadSafety;
051import com.unboundid.util.ThreadSafetyLevel;
052
053import static com.unboundid.util.ssl.cert.CertMessages.*;
054
055
056
057/**
058 * This enum defines a set of OIDs and algorithm names for password-based
059 * cryptography as described in the PKCS #5 specification defined in RFC 8018.
060 */
061@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
062public enum PKCS5AlgorithmIdentifier
063{
064  /**
065   * The algorithm identifier for the PBES2 encryption scheme.  This scheme is
066   * defined in RFC 8018 section 6.2, and the identifier is defined in appendix
067   * A.4 of that specification.
068   */
069  PBES2("1.2.840.113549.1.5.13", "PBES2", Collections.<String>emptySet(),
070       INFO_PKCS5_ALG_ID_DESC_PBES2.get()),
071
072
073
074  /**
075   * The algorithm identifier for the PBKDF2 key derivation function, which is
076   * intended to be used by the PBES2 encryption scheme.  This identifier is
077   * described in RFC 8018 appendix A.2.
078   */
079  PBKDF2("1.2.840.113549.1.5.12", "PBKDF2", Collections.<String>emptySet(),
080       INFO_PKCS5_ALG_ID_DESC_PBKDF2.get()),
081
082
083
084  /**
085   * The algorithm identifier for the HMAC-SHA-1 pseudorandom function, which
086   * may be used in conjunction with the PBKDF2 key derivation function.  This
087   * identifier is described in RFC 8018 appendix B.1.1.
088   */
089  HMAC_SHA_1("1.2.840.113549.2.7", "HMAC-SHA-1",
090       StaticUtils.setOf("HMAC-SHA", "HmacSHA"),
091       INFO_PKCS5_ALG_ID_DESC_HMAC_SHA_1.get()),
092
093
094
095  /**
096   * The algorithm identifier for the HMAC-SHA-224 pseudorandom function, which
097   * may be used in conjunction with the PBKDF2 key derivation function.  This
098   * identifier is described in RFC 8018 appendix B.1.2.
099   */
100  HMAC_SHA_224("1.2.840.113549.2.8", "HMAC-SHA-224",
101       StaticUtils.setOf("HmacSHA224"),
102       INFO_PKCS5_ALG_ID_DESC_HMAC_SHA_224.get()),
103
104
105
106  /**
107   * The algorithm identifier for the HMAC-SHA-256 pseudorandom function, which
108   * may be used in conjunction with the PBKDF2 key derivation function.  This
109   * identifier is described in RFC 8018 appendix B.1.2.
110   */
111  HMAC_SHA_256("1.2.840.113549.2.9", "HMAC-SHA-256",
112       StaticUtils.setOf("HmacSHA256"),
113       INFO_PKCS5_ALG_ID_DESC_HMAC_SHA_256.get()),
114
115
116
117  /**
118   * The algorithm identifier for the HMAC-SHA-384 pseudorandom function, which
119   * may be used in conjunction with the PBKDF2 key derivation function.  This
120   * identifier is described in RFC 8018 appendix B.1.2.
121   */
122  HMAC_SHA_384("1.2.840.113549.2.10", "HMAC-SHA-384",
123       StaticUtils.setOf("HmacSHA384"),
124       INFO_PKCS5_ALG_ID_DESC_HMAC_SHA_384.get()),
125
126
127
128  /**
129   * The algorithm identifier for the HMAC-SHA-512 pseudorandom function, which
130   * may be used in conjunction with the PBKDF2 key derivation function.  This
131   * identifier is described in RFC 8018 appendix B.1.2.
132   */
133  HMAC_SHA_512("1.2.840.113549.2.11", "HMAC-SHA-512",
134       StaticUtils.setOf("HmacSHA512"),
135       INFO_PKCS5_ALG_ID_DESC_HMAC_SHA_512.get()),
136
137
138
139  /**
140   * The algorithm identifier for the DESede/CBC/PKCS5Padding cipher
141   * transformation.  This identifier is described in RFC 8018 appendix B.2.2.
142   */
143  DES_EDE3_CBC_PAD("1.2.840.113549.3.7", "DES-EDE3-CBC-PAD",
144       StaticUtils.setOf("DES-EDE-CBC-PAD", "DES-EDE3-CBC", "DES-EDE-CBC",
145            "DES-EDE3", "DESEDE3", "DES-EDE", "DESEDE",
146            "3DES-CBC-PAD", "3DES-CBC", "3DES"),
147       INFO_PKCS5_ALG_ID_DESC_DES_EDE_CBC_PAD.get()),
148
149
150
151  /**
152   * The algorithm identifier for the 128-bit AES/CBC/PKCS5Padding cipher
153   * transformation.  This identifier is described in RFC 8018 appendix B.2.2.
154   */
155  AES_128_CBC_PAD("2.16.840.1.101.3.4.1.2", "AES-128-CBC-PAD",
156       StaticUtils.setOf("AES128-CBC", "AES128", "AES",
157            "AES/CBC/PKCS5Padding", "AES128/CBC/PKCS5Padding"),
158       INFO_PKCS5_ALG_ID_DESC_AES_128_CBC_PAD.get()),
159
160
161
162  /**
163   * The algorithm identifier for the 192-bit AES/CBC/PKCS5Padding cipher
164   * transformation.  This identifier is described in RFC 8018 appendix C.
165   */
166  AES_192_CBC_PAD("2.16.840.1.101.3.4.1.22", "AES-192-CBC-PAD",
167       StaticUtils.setOf("AES192-CBC", "AES192",  "AES192/CBC/PKCS5Padding"),
168       INFO_PKCS5_ALG_ID_DESC_AES_192_CBC_PAD.get()),
169
170
171
172  /**
173   * The algorithm identifier for the 256-bit AES/CBC/PKCS5Padding cipher
174   * transformation.  This identifier is described in RFC 8018 appendix C.
175   */
176  AES_256_CBC_PAD("2.16.840.1.101.3.4.1.42", "AES-256-CBC-PAD",
177       StaticUtils.setOf("AES256-CBC", "AES256",  "AES256/CBC/PKCS5Padding"),
178       INFO_PKCS5_ALG_ID_DESC_AES_256_CBC_PAD.get());
179
180
181
182  /**
183   * Retrieve a map of pseudorandom functions defined in this set of PKCS #5
184   * algorithm identifiers.  The value for each item in the map will be the
185   * name of the secret key factory algorithm that corresponds to the PBKDF2
186   * variant that uses the specified function.
187   */
188  @NotNull private static final Map<PKCS5AlgorithmIdentifier,String>
189       PSEUDORANDOM_FUNCTIONS = StaticUtils.mapOf(
190            HMAC_SHA_1, "PBKDF2WithHmacSHA1",
191            HMAC_SHA_224, "PBKDF2WithHmacSHA224",
192            HMAC_SHA_256, "PBKDF2WithHmacSHA256",
193            HMAC_SHA_384, "PBKDF2WithHmacSHA384",
194            HMAC_SHA_512, "PBKDF2WithHmacSHA512");
195
196
197
198  /**
199   * A map of information about cipher transformations defined in this set of
200   * PKCS #5 algorithm identifiers.  The value for each item in the map is an
201   * object pair in which the first element is the name of the cipher
202   * transformation and the second element is the expected key size, in bits.
203   */
204  @NotNull()
205  private static final Map<PKCS5AlgorithmIdentifier,ObjectPair<String,Integer>>
206       CIPHER_TRANSFORMATIONS = StaticUtils.mapOf(
207            DES_EDE3_CBC_PAD, new ObjectPair<>("DESede/CBC/PKCS5Padding", 192),
208            AES_128_CBC_PAD, new ObjectPair<>("AES/CBC/PKCS5Padding", 128),
209            AES_192_CBC_PAD, new ObjectPair<>("AES/CBC/PKCS5Padding", 192),
210            AES_256_CBC_PAD, new ObjectPair<>("AES/CBC/PKCS5Padding", 256));
211
212
213
214  // The OID for this identifier.
215  @NotNull private final OID oid;
216
217  // A set of prepared names that may be used to reference this algorithm
218  // identifier.
219  @NotNull private final Set<String> preparedNames;
220
221  // A human-readable description for the associated algorithm.
222  @NotNull private final String description;
223
224  // The primary name for the associated algorithm.
225  @NotNull private final String primaryName;
226
227
228
229  /**
230   * Creates a new PKCS #5 algorithm identifier with the provided information.
231   *
232   * @param  oidString         The string representation of the OID for this
233   *                           algorithm identifier.  It must not be
234   *                           {@code null} and must represent a valid OID.
235   * @param  primaryName       The primary name for this algorithm identifier.
236   *                           It must not be {@code null}.
237   * @param  alternativeNames  A set of alternative names for this algorithm
238   *                           identifier.  It must not be {@code null}, but may
239   *                           be empty.
240   * @param  description       A human-readable description for the associated
241   *                           algorithm.
242   */
243  PKCS5AlgorithmIdentifier(@NotNull final String oidString,
244                           @NotNull final String primaryName,
245                           @NotNull final Set<String> alternativeNames,
246                           @NotNull final String description)
247  {
248    this.primaryName = primaryName;
249    this.description = description;
250
251    final Set<String> preparedNameSet = new HashSet<>();
252    preparedNameSet.add(prepareName(primaryName));
253    for (final String alternativeName : alternativeNames)
254    {
255      preparedNameSet.add(prepareName(alternativeName));
256    }
257
258    preparedNames = Collections.unmodifiableSet(preparedNameSet);
259
260    oid = new OID(oidString);
261  }
262
263
264
265  /**
266   * Retrieves the OID for this PKCS #5 algorithm identifier.
267   *
268   * @return  The OID for this PKCS #5 algorithm identifier.
269   */
270  @NotNull()
271  public OID getOID()
272  {
273    return oid;
274  }
275
276
277
278  /**
279   * Retrieves the name for the algorithm.
280   *
281   * @return  The name for the algorithm.
282   */
283  @NotNull()
284  public String getName()
285  {
286    return primaryName;
287  }
288
289
290
291  /**
292   * Retrieves a human-readable description for the algorithm.
293   *
294   * @return  A human-readable description for the algorithm.
295   */
296  @NotNull()
297  public String getDescription()
298  {
299    return description;
300  }
301
302
303
304  /**
305   * Retrieves the PKCS #5 algorithm identifier with the specified OID.
306   *
307   * @param  oid  The OID for the PKCS #5 algorithm identifier instance to
308   *              retrieve.  It must not be {@code null}.
309   *
310   * @return  The appropriate PKCS #5 algorithm identifier instance, or
311   *          {@code null} if the provided OID does not reference a known PKCS
312   *          #5 algorithm identifier.
313   */
314  @Nullable()
315  public static PKCS5AlgorithmIdentifier forOID(@NotNull final OID oid)
316  {
317    for (final PKCS5AlgorithmIdentifier v : values())
318    {
319      if (v.oid.equals(oid))
320      {
321        return v;
322      }
323    }
324
325    return null;
326  }
327
328
329
330  /**
331   * Retrieves the PKCS #5 algorithm identifier with the specified name.
332   *
333   * @param  name  The name for the PKCS #5 algorithm identifier to retrieve.
334   *               It must not be {@code null}.
335   *
336   * @return  The appropriate PKCS #5 algorithm identifier instance, or
337   *          {@code null} if the provided name does not reference a known PKCS
338   *          #5 algorithm identifier.
339   */
340  @Nullable()
341  public static PKCS5AlgorithmIdentifier forName(@NotNull final String name)
342  {
343    final String preparedName = prepareName(name);
344    for (final PKCS5AlgorithmIdentifier v : values())
345    {
346      if (v.preparedNames.contains(preparedName))
347      {
348        return v;
349      }
350    }
351
352    return null;
353  }
354
355
356
357  /**
358   * Prepares the provided name to be used by the {@link #forName(String)}
359   * method.  All spaces, dashes, underscores, and forward slashes will be
360   * removed.
361   *
362   * @param  name  The name to be compared.
363   *
364   * @return  The prepared version of the provided name.
365   */
366  @NotNull()
367  private static String prepareName(@NotNull final String name)
368  {
369    final StringBuilder buffer = new StringBuilder(name.length());
370
371    for (final char c : name.toLowerCase().toCharArray())
372    {
373      switch (c)
374      {
375        case ' ':
376        case '-':
377        case '_':
378        case '/':
379          // This character will be omitted.
380          break;
381        default:
382          // This character will be used.
383          buffer.append(c);
384      }
385    }
386
387    return buffer.toString();
388  }
389
390
391
392  /**
393   * Retrieves the human-readable name for the PKCS #5 algorithm identifier
394   * value with the provided OID, or a string representation of the OID if there
395   * is no value with that OID.
396   *
397   * @param  oid  The OID for the PKCS #5 algorithm identifier to retrieve.
398   *
399   * @return  The human-readable name for the PKCS #5 algorithm identifier value
400   *          with the provided OID, or a string representation of the OID if
401   *          there is no value with that OID.
402   */
403  @NotNull()
404  public static String getNameOrOID(@NotNull final OID oid)
405  {
406    final PKCS5AlgorithmIdentifier id = forOID(oid);
407    if (id == null)
408    {
409      return oid.toString();
410    }
411    else
412    {
413      return id.primaryName;
414    }
415  }
416
417
418
419  /**
420   * Retrieves the set of PKCS #5 algorithm identifiers that represent
421   * pseudorandom functions.
422   *
423   * @return  The set of PKCS #5 algorithm identifiers that represent
424   *          pseudorandom functions.
425   */
426  @NotNull()
427  public static Set<PKCS5AlgorithmIdentifier> getPseudorandomFunctions()
428  {
429    return PSEUDORANDOM_FUNCTIONS.keySet();
430  }
431
432
433
434  /**
435   * Retrieves the name of the secret key factory algorithm that should be used
436   * to create a PBKDF2 key factory that uses the specified pseudorandom
437   * function.
438   *
439   * @param  identifier  The PKCS #5 algorithm identifier that represents the
440   *                     pseudorandom function for which to obtain the name of
441   *                     the corresponding PBKDF2 secret key factory algorithm.
442   *                     It must not be {@code null}.
443   *
444   * @return  The name of the PBKDF2 key factory algorithm that uses the
445   *          specified pseudorandom function, or {@code null} if the provided
446   *          identifier does not represent a known pseudorandom function.
447   */
448  @Nullable()
449  public static String
450              getPBKDF2SecretKeyFactoryAlgorithmForPseudorandomFunction(
451                   @NotNull final PKCS5AlgorithmIdentifier identifier)
452  {
453    return PSEUDORANDOM_FUNCTIONS.get(identifier);
454  }
455
456
457
458  /**
459   * Retrieves the set of PKCS #5 algorithm identifiers that represent cipher
460   * transformations.
461   *
462   * @return  The set of PKCS #5 algorithm identifiers that represent cipher
463   *          transformations.
464   */
465  @NotNull()
466  public static Set<PKCS5AlgorithmIdentifier> getCipherTransformations()
467  {
468    return CIPHER_TRANSFORMATIONS.keySet();
469  }
470
471
472
473  /**
474   * Retrieves the name of the cipher algorithm that should be used when
475   * creating a secret key for the specified cipher transformation.
476   *
477   * @param  identifier  The PKCS #5 algorithm identifier that represents the
478   *                     cipher transformation for which to obtain the name of
479   *                     the corresponding cipher algorithm.  It must not be
480   *                     {@code null}.
481   *
482   * @return  The name of the cipher algorithm that should be used when creating
483   *          a secret key for the specified cipher transformation, or
484   *          {@code null} if the provided identifier does not represent a known
485   *          cipher transformation.
486   */
487  @Nullable()
488  public static String getCipherAlgorithmName(
489              @NotNull final PKCS5AlgorithmIdentifier identifier)
490  {
491    final ObjectPair<String,Integer> cipherTransformationPair =
492         CIPHER_TRANSFORMATIONS.get(identifier);
493    if (cipherTransformationPair == null)
494    {
495      return null;
496    }
497
498    final String cipherTransformationName = cipherTransformationPair.getFirst();
499    final int slashPos = cipherTransformationName.indexOf('/');
500    return cipherTransformationName.substring(0, slashPos);
501  }
502
503
504
505  /**
506   * Retrieves the name of the cipher transformation that should be used when
507   * creating a cipher instance for the specified cipher transformation.
508   *
509   * @param  identifier  The PKCS #5 algorithm identifier that represents the
510   *                     cipher transformation for which to obtain the name.  It
511   *                     must not be {@code null}.
512   *
513   * @return  The name of the cipher transformation that should be used when
514   *          creating a cipher instance for the specified cipher
515   *          transformation, or {@code null} if the provided identifier does
516   *          not represent a known cipher transformation.
517   */
518  @Nullable()
519  public static String getCipherTransformationName(
520              @NotNull final PKCS5AlgorithmIdentifier identifier)
521  {
522    final ObjectPair<String,Integer> cipherTransformationPair =
523         CIPHER_TRANSFORMATIONS.get(identifier);
524    if (cipherTransformationPair == null)
525    {
526      return null;
527    }
528
529    return cipherTransformationPair.getFirst();
530  }
531
532
533
534  /**
535   * Retrieves the key size, in bits, that should be used when creating a
536   * secret key for the specified cipher transformation.
537   *
538   * @param  identifier  The PKCS #5 algorithm identifier that represents the
539   *                     cipher transformation for which to obtain the key size.
540   *                     It must not be {@code null}.
541   *
542   * @return  The key size, in bits, that should be used when creating a secret
543   *          key for the specified cipher transformation, or {@code null} if
544   *          the provided identifier does not represent a known cipher
545   *          transformation.
546   */
547  @Nullable()
548  public static Integer getCipherKeySizeBits(
549              @NotNull final PKCS5AlgorithmIdentifier identifier)
550  {
551    final ObjectPair<String,Integer> cipherTransformationPair =
552         CIPHER_TRANSFORMATIONS.get(identifier);
553    if (cipherTransformationPair == null)
554    {
555      return null;
556    }
557
558    return cipherTransformationPair.getSecond();
559  }
560
561
562
563  /**
564   * Retrieves a string representation of this PKCS #5 algorithm identifier.
565   *
566   * @return  A string representation of this PKCS #5 algorithm identifier.
567   */
568  @Override()
569  @NotNull()
570  public String toString()
571  {
572    return primaryName;
573  }
574}