1 /*
  2  * Copyright (c) 1996, 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 sun.security.x509;
 27 
 28 import java.io.*;
 29 import java.security.interfaces.RSAKey;
 30 import java.security.spec.AlgorithmParameterSpec;
 31 import java.security.spec.EdDSAParameterSpec;
 32 import java.security.spec.InvalidParameterSpecException;
 33 import java.security.spec.MGF1ParameterSpec;
 34 import java.security.spec.PSSParameterSpec;
 35 import java.util.*;
 36 import java.util.concurrent.ConcurrentHashMap;
 37 import java.security.*;
 38 import java.security.interfaces.*;
 39 
 40 import sun.security.rsa.PSSParameters;
 41 import sun.security.util.*;
 42 
 43 
 44 /**
 45  * This class identifies algorithms, such as cryptographic transforms, each
 46  * of which may be associated with parameters.  Instances of this base class
 47  * are used when this runtime environment has no special knowledge of the
 48  * algorithm type, and may also be used in other cases.  Equivalence is
 49  * defined according to OID and (where relevant) parameters.
 50  *
 51  * <P>Subclasses may be used, for example when the algorithm ID has
 52  * associated parameters which some code (e.g. code using public keys) needs
 53  * to have parsed.  Two examples of such algorithms are Diffie-Hellman key
 54  * exchange, and the Digital Signature Standard Algorithm (DSS/DSA).
 55  *
 56  * <P>The OID constants defined in this class correspond to some widely
 57  * used algorithms, for which conventional string names have been defined.
 58  * This class is not a general repository for OIDs, or for such string names.
 59  * Note that the mappings between algorithm IDs and algorithm names is
 60  * not one-to-one.
 61  *
 62  *
 63  * @author David Brownell
 64  * @author Amit Kapoor
 65  * @author Hemma Prafullchandra
 66  */
 67 public class AlgorithmId implements Serializable, DerEncoder {
 68 
 69     /** use serialVersionUID from JDK 1.1. for interoperability */
 70     @java.io.Serial
 71     private static final long serialVersionUID = 7205873507486557157L;
 72 
 73     /**
 74      * The object identitifer being used for this algorithm.
 75      */
 76     private ObjectIdentifier algid;
 77 
 78     // The (parsed) parameters
 79     @SuppressWarnings("serial") // Not statically typed as Serializable
 80     private AlgorithmParameters algParams;
 81     private boolean constructedFromDer = true;
 82 
 83     /**
 84      * Parameters for this algorithm.  These are stored in unparsed
 85      * DER-encoded form; subclasses can be made to automaticaly parse
 86      * them so there is fast access to these parameters.
 87      */
 88     @SuppressWarnings("serial") // Not statically typed as Serializable
 89     protected DerValue          params;
 90 
 91 
 92     /**
 93      * Constructs an algorithm ID which will be initialized
 94      * separately, for example by deserialization.
 95      * @deprecated use one of the other constructors.
 96      */
 97     @Deprecated
 98     public AlgorithmId() { }
 99 
100     /**
101      * Constructs a parameterless algorithm ID.
102      *
103      * @param oid the identifier for the algorithm
104      */
105     public AlgorithmId(ObjectIdentifier oid) {
106         algid = oid;
107     }
108 
109     /**
110      * Constructs an algorithm ID with algorithm parameters.
111      *
112      * @param oid the identifier for the algorithm.
113      * @param algparams the associated algorithm parameters.
114      */
115     public AlgorithmId(ObjectIdentifier oid, AlgorithmParameters algparams) {
116         algid = oid;
117         algParams = algparams;
118         constructedFromDer = false;
119     }
120 
121     private AlgorithmId(ObjectIdentifier oid, DerValue params)
122             throws IOException {
123         this.algid = oid;
124         this.params = params;
125         if (this.params != null) {
126             decodeParams();
127         }
128     }
129 
130     protected void decodeParams() throws IOException {
131         String algidName = getName();
132         try {
133             algParams = AlgorithmParameters.getInstance(algidName);
134         } catch (NoSuchAlgorithmException e) {
135             /*
136              * This algorithm parameter type is not supported, so we cannot
137              * parse the parameters.
138              */
139             algParams = null;
140             return;
141         }
142 
143         // Decode (parse) the parameters
144         algParams.init(params.toByteArray());
145     }
146 
147     /**
148      * Marshal a DER-encoded "AlgorithmID" sequence on the DER stream.
149      */
150     public final void encode(DerOutputStream out) throws IOException {
151         derEncode(out);
152     }
153 
154     /**
155      * DER encode this object onto an output stream.
156      * Implements the <code>DerEncoder</code> interface.
157      *
158      * @param out
159      * the output stream on which to write the DER encoding.
160      *
161      * @exception IOException on encoding error.
162      */
163     public void derEncode (OutputStream out) throws IOException {
164         DerOutputStream bytes = new DerOutputStream();
165         DerOutputStream tmp = new DerOutputStream();
166 
167         bytes.putOID(algid);
168         // Setup params from algParams since no DER encoding is given
169         if (constructedFromDer == false) {
170             if (algParams != null) {
171                 params = new DerValue(algParams.getEncoded());
172             } else {
173                 params = null;
174             }
175         }
176         if (params == null) {
177             // Changes backed out for compatibility with Solaris
178 
179             // Several AlgorithmId should omit the whole parameter part when
180             // it's NULL. They are ---
181             // RFC 3370 2.1: Implementations SHOULD generate SHA-1
182             // AlgorithmIdentifiers with absent parameters.
183             // RFC 3447 C1: When id-sha1, id-sha224, id-sha256, id-sha384 and
184             // id-sha512 are used in an AlgorithmIdentifier the parameters
185             // (which are optional) SHOULD be omitted.
186             // RFC 3279 2.3.2: The id-dsa algorithm syntax includes optional
187             // domain parameters... When omitted, the parameters component
188             // MUST be omitted entirely
189             // RFC 3370 3.1: When the id-dsa-with-sha1 algorithm identifier
190             // is used, the AlgorithmIdentifier parameters field MUST be absent.
191             /*if (
192                 algid.equals((Object)SHA_oid) ||
193                 algid.equals((Object)SHA224_oid) ||
194                 algid.equals((Object)SHA256_oid) ||
195                 algid.equals((Object)SHA384_oid) ||
196                 algid.equals((Object)SHA512_oid) ||
197                 algid.equals((Object)SHA512_224_oid) ||
198                 algid.equals((Object)SHA512_256_oid) ||
199                 algid.equals((Object)SHA3_224_oid) ||
200                 algid.equals((Object)SHA3_256_oid) ||
201                 algid.equals((Object)SHA3_384_oid) ||
202                 algid.equals((Object)SHA3_512_oid) ||
203                 algid.equals((Object)DSA_oid) ||
204                 algid.equals((Object)sha1WithDSA_oid)) {
205                 ; // no parameter part encoded
206             } else {
207                 bytes.putNull();
208             }*/
209             if (algid.equals(RSASSA_PSS_oid) || algid.equals(ed448_oid)
210                     || algid.equals(ed25519_oid)) {
211                 // RFC 4055 3.3: when an RSASSA-PSS key does not require
212                 // parameter validation, field is absent.
213             } else {
214                 bytes.putNull();
215             }
216         } else {
217             bytes.putDerValue(params);
218         }
219         tmp.write(DerValue.tag_Sequence, bytes);
220         out.write(tmp.toByteArray());
221     }
222 
223 
224     /**
225      * Returns the DER-encoded X.509 AlgorithmId as a byte array.
226      */
227     public final byte[] encode() throws IOException {
228         DerOutputStream out = new DerOutputStream();
229         derEncode(out);
230         return out.toByteArray();
231     }
232 
233     /**
234      * Returns the ISO OID for this algorithm.  This is usually converted
235      * to a string and used as part of an algorithm name, for example
236      * "OID.1.3.14.3.2.13" style notation.  Use the <code>getName</code>
237      * call when you do not need to ensure cross-system portability
238      * of algorithm names, or need a user friendly name.
239      */
240     public final ObjectIdentifier getOID () {
241         return algid;
242     }
243 
244     /**
245      * Returns a name for the algorithm which may be more intelligible
246      * to humans than the algorithm's OID, but which won't necessarily
247      * be comprehensible on other systems.  For example, this might
248      * return a name such as "MD5withRSA" for a signature algorithm on
249      * some systems.  It also returns names like "OID.1.2.3.4", when
250      * no particular name for the algorithm is known.
251      *
252      * Note: for ecdsa-with-SHA2 plus hash algorithm (Ex: SHA-256), this method
253      * returns the "full" signature algorithm (Ex: SHA256withECDSA) directly.
254      */
255     public String getName() {
256         String oidStr = algid.toString();
257         // first check the list of support oids
258         KnownOIDs o = KnownOIDs.findMatch(oidStr);
259         if (o == KnownOIDs.SpecifiedSHA2withECDSA) {
260             if (params != null) {
261                 try {
262                     AlgorithmId paramsId =
263                         AlgorithmId.parse(new DerValue(params.toByteArray()));
264                     String paramsName = paramsId.getName();
265                     return makeSigAlg(paramsName, "EC");
266                 } catch (IOException e) {
267                     // ignore
268                 }
269             }
270         }
271         if (o != null) {
272             return o.stdName();
273         } else {
274             String n = aliasOidsTable().get(oidStr);
275             if (n != null) {
276                 return n;
277             } else {
278                 return algid.toString();
279             }
280         }
281     }
282 
283     public AlgorithmParameters getParameters() {
284         return algParams;
285     }
286 
287     /**
288      * Returns the DER encoded parameter, which can then be
289      * used to initialize java.security.AlgorithmParameters.
290      *
291      * Note: for ecdsa-with-SHA2 plus hash algorithm (Ex: SHA-256), this method
292      * returns null because {@link #getName()} has already returned the "full"
293      * signature algorithm (Ex: SHA256withECDSA).
294      *
295      * @return DER encoded parameters, or null not present.
296      */
297     public byte[] getEncodedParams() throws IOException {
298         return (params == null ||
299             algid.toString().equals(KnownOIDs.SpecifiedSHA2withECDSA.value()))
300                 ? null
301                 : params.toByteArray();
302     }
303 
304     /**
305      * Returns true iff the argument indicates the same algorithm
306      * with the same parameters.
307      */
308     public boolean equals(AlgorithmId other) {
309         boolean paramsEqual = Objects.equals(other.params, params);
310         return (algid.equals((Object)other.algid) && paramsEqual);
311     }
312 
313     /**
314      * Compares this AlgorithmID to another.  If algorithm parameters are
315      * available, they are compared.  Otherwise, just the object IDs
316      * for the algorithm are compared.
317      *
318      * @param other preferably an AlgorithmId, else an ObjectIdentifier
319      */
320     public boolean equals(Object other) {
321         if (this == other) {
322             return true;
323         }
324         if (other instanceof AlgorithmId) {
325             return equals((AlgorithmId) other);
326         } else if (other instanceof ObjectIdentifier) {
327             return equals((ObjectIdentifier) other);
328         } else {
329             return false;
330         }
331     }
332 
333     /**
334      * Compares two algorithm IDs for equality.  Returns true iff
335      * they are the same algorithm, ignoring algorithm parameters.
336      */
337     public final boolean equals(ObjectIdentifier id) {
338         return algid.equals((Object)id);
339     }
340 
341     /**
342      * Returns a hashcode for this AlgorithmId.
343      *
344      * @return a hashcode for this AlgorithmId.
345      */
346     public int hashCode() {
347         StringBuilder sbuf = new StringBuilder();
348         sbuf.append(algid.toString());
349         sbuf.append(paramsToString());
350         return sbuf.toString().hashCode();
351     }
352 
353     /**
354      * Provides a human-readable description of the algorithm parameters.
355      * This may be redefined by subclasses which parse those parameters.
356      */
357     protected String paramsToString() {
358         if (params == null) {
359             return "";
360         } else if (algParams != null) {
361             return algParams.toString();
362         } else {
363             return ", params unparsed";
364         }
365     }
366 
367     /**
368      * Returns a string describing the algorithm and its parameters.
369      */
370     public String toString() {
371         return getName() + paramsToString();
372     }
373 
374     /**
375      * Parse (unmarshal) an ID from a DER sequence input value.  This form
376      * parsing might be used when expanding a value which has already been
377      * partially unmarshaled as a set or sequence member.
378      *
379      * @exception IOException on error.
380      * @param val the input value, which contains the algid and, if
381      *          there are any parameters, those parameters.
382      * @return an ID for the algorithm.  If the system is configured
383      *          appropriately, this may be an instance of a class
384      *          with some kind of special support for this algorithm.
385      *          In that case, you may "narrow" the type of the ID.
386      */
387     public static AlgorithmId parse(DerValue val) throws IOException {
388         if (val.tag != DerValue.tag_Sequence) {
389             throw new IOException("algid parse error, not a sequence");
390         }
391 
392         /*
393          * Get the algorithm ID and any parameters.
394          */
395         ObjectIdentifier        algid;
396         DerValue                params;
397         DerInputStream          in = val.toDerInputStream();
398 
399         algid = in.getOID();
400         if (in.available() == 0) {
401             params = null;
402         } else {
403             params = in.getDerValue();
404             if (params.tag == DerValue.tag_Null) {
405                 if (params.length() != 0) {
406                     throw new IOException("invalid NULL");
407                 }
408                 params = null;
409             }
410             if (in.available() != 0) {
411                 throw new IOException("Invalid AlgorithmIdentifier: extra data");
412             }
413         }
414 
415         return new AlgorithmId(algid, params);
416     }
417 
418     /**
419      * Returns one of the algorithm IDs most commonly associated
420      * with this algorithm name.
421      *
422      * @param algname the name being used
423      * @deprecated use the short get form of this method.
424      * @exception NoSuchAlgorithmException on error.
425      */
426     @Deprecated
427     public static AlgorithmId getAlgorithmId(String algname)
428             throws NoSuchAlgorithmException {
429         return get(algname);
430     }
431 
432     /**
433      * Returns one of the algorithm IDs most commonly associated
434      * with this algorithm name.
435      *
436      * @param algname the name being used
437      * @exception NoSuchAlgorithmException on error.
438      */
439     public static AlgorithmId get(String algname)
440             throws NoSuchAlgorithmException {
441         ObjectIdentifier oid;
442         try {
443             oid = algOID(algname);
444         } catch (IOException ioe) {
445             throw new NoSuchAlgorithmException
446                 ("Invalid ObjectIdentifier " + algname);
447         }
448 
449         if (oid == null) {
450             throw new NoSuchAlgorithmException
451                 ("unrecognized algorithm name: " + algname);
452         }
453         return new AlgorithmId(oid);
454     }
455 
456     /**
457      * Returns one of the algorithm IDs most commonly associated
458      * with this algorithm parameters.
459      *
460      * @param algparams the associated algorithm parameters.
461      * @exception NoSuchAlgorithmException on error.
462      */
463     public static AlgorithmId get(AlgorithmParameters algparams)
464             throws NoSuchAlgorithmException {
465         ObjectIdentifier oid;
466         String algname = algparams.getAlgorithm();
467         try {
468             oid = algOID(algname);
469         } catch (IOException ioe) {
470             throw new NoSuchAlgorithmException
471                 ("Invalid ObjectIdentifier " + algname);
472         }
473         if (oid == null) {
474             throw new NoSuchAlgorithmException
475                 ("unrecognized algorithm name: " + algname);
476         }
477         return new AlgorithmId(oid, algparams);
478     }
479 
480     /*
481      * Translates from some common algorithm names to the
482      * OID with which they're usually associated ... this mapping
483      * is the reverse of the one below, except in those cases
484      * where synonyms are supported or where a given algorithm
485      * is commonly associated with multiple OIDs.
486      *
487      * XXX This method needs to be enhanced so that we can also pass the
488      * scope of the algorithm name to it, e.g., the algorithm name "DSA"
489      * may have a different OID when used as a "Signature" algorithm than when
490      * used as a "KeyPairGenerator" algorithm.
491      */
492     private static ObjectIdentifier algOID(String name) throws IOException {
493         if (name.startsWith("OID.")) {
494             name = name.substring("OID.".length());
495         }
496 
497         KnownOIDs k = KnownOIDs.findMatch(name);
498         if (k != null) {
499             return ObjectIdentifier.of(k);
500         }
501 
502         // unknown algorithm oids
503         if (name.indexOf(".") == -1) {
504             // see if there is a matching oid string alias mapping from
505             // 3rd party providers
506             name = name.toUpperCase(Locale.ENGLISH);
507             String oidStr = aliasOidsTable().get(name);
508             if (oidStr != null) {
509                 return ObjectIdentifier.of(oidStr);
510             } return null;
511         } else {
512             return ObjectIdentifier.of(name);
513         }
514     }
515 
516     // oid string cache index'ed by algorithm name and oid strings
517     private static volatile Map<String,String> aliasOidsTable;
518 
519     // returns the aliasOidsTable, lazily initializing it on first access.
520     private static Map<String,String> aliasOidsTable() {
521         // Double checked locking; safe because aliasOidsTable is volatile
522         Map<String,String> tab = aliasOidsTable;
523         if (tab == null) {
524             synchronized (AlgorithmId.class) {
525                 if ((tab = aliasOidsTable) == null) {
526                     aliasOidsTable = tab = collectOIDAliases();
527                 }
528             }
529         }
530         return tab;
531     }
532 
533     private static boolean isKnownProvider(Provider p) {
534         String pn = p.getName();
535         String mn = p.getClass().getModule().getName();
536         if (pn != null && mn != null) {
537             return ((mn.equals("java.base") &&
538                     (pn.equals("SUN") || pn.equals("SunRsaSign") ||
539                     pn.equals("SunJCE") || pn.equals("SunJSSE"))) ||
540                 (mn.equals("jdk.crypto.ec") && pn.equals("SunEC")) ||
541                 (mn.equals("jdk.crypto.mscapi") && pn.equals("SunMSCAPI")) ||
542                 (mn.equals("jdk.crypto.cryptoki") &&
543                     pn.startsWith("SunPKCS11")));
544         } else {
545             return false;
546         }
547     }
548 
549     private static ConcurrentHashMap<String, String> collectOIDAliases() {
550         ConcurrentHashMap<String, String> t = new ConcurrentHashMap<>();
551         for (Provider provider : Security.getProviders()) {
552             // skip providers which are already using SecurityProviderConstants
553             // and KnownOIDs
554             if (isKnownProvider(provider)) {
555                 continue;
556             }
557             for (Object key : provider.keySet()) {
558                 String alias = (String)key;
559                 String upperCaseAlias = alias.toUpperCase(Locale.ENGLISH);
560                 int index;
561                 if (upperCaseAlias.startsWith("ALG.ALIAS") &&
562                     (index = upperCaseAlias.indexOf("OID.", 0)) != -1) {
563                     index += "OID.".length();
564                     if (index == alias.length()) {
565                         // invalid alias entry
566                         break;
567                     }
568                     String ostr = alias.substring(index);
569                     String stdAlgName = provider.getProperty(alias);
570                     if (stdAlgName != null) {
571                         stdAlgName = stdAlgName.toUpperCase(Locale.ENGLISH);
572                     }
573                     // add the name->oid and oid->name mappings if none exists
574                     if (KnownOIDs.findMatch(stdAlgName) == null) {
575                         // not override earlier entries if it exists
576                         t.putIfAbsent(stdAlgName, ostr);
577                     }
578                     if (KnownOIDs.findMatch(ostr) == null) {
579                         // not override earlier entries if it exists
580                         t.putIfAbsent(ostr, stdAlgName);
581                     }
582                 }
583             }
584         }
585         return t;
586     }
587 
588     public static final ObjectIdentifier MD2_oid =
589             ObjectIdentifier.of(KnownOIDs.MD2);
590 
591     public static final ObjectIdentifier MD5_oid =
592             ObjectIdentifier.of(KnownOIDs.MD5);
593 
594     public static final ObjectIdentifier SHA_oid =
595             ObjectIdentifier.of(KnownOIDs.SHA_1);
596 
597     public static final ObjectIdentifier SHA224_oid =
598             ObjectIdentifier.of(KnownOIDs.SHA_224);
599 
600     public static final ObjectIdentifier SHA256_oid =
601             ObjectIdentifier.of(KnownOIDs.SHA_256);
602 
603     public static final ObjectIdentifier SHA384_oid =
604             ObjectIdentifier.of(KnownOIDs.SHA_384);
605 
606     public static final ObjectIdentifier SHA512_oid =
607             ObjectIdentifier.of(KnownOIDs.SHA_512);
608 
609     public static final ObjectIdentifier SHA512_224_oid =
610             ObjectIdentifier.of(KnownOIDs.SHA_512$224);
611 
612     public static final ObjectIdentifier SHA512_256_oid =
613             ObjectIdentifier.of(KnownOIDs.SHA_512$256);
614 
615     public static final ObjectIdentifier SHA3_224_oid =
616             ObjectIdentifier.of(KnownOIDs.SHA3_224);
617 
618     public static final ObjectIdentifier SHA3_256_oid =
619             ObjectIdentifier.of(KnownOIDs.SHA3_256);
620 
621     public static final ObjectIdentifier SHA3_384_oid =
622             ObjectIdentifier.of(KnownOIDs.SHA3_384);
623 
624     public static final ObjectIdentifier SHA3_512_oid =
625             ObjectIdentifier.of(KnownOIDs.SHA3_512);
626 
627     public static final ObjectIdentifier DSA_oid =
628             ObjectIdentifier.of(KnownOIDs.DSA);
629 
630     public static final ObjectIdentifier EC_oid =
631             ObjectIdentifier.of(KnownOIDs.EC);
632 
633     public static final ObjectIdentifier RSAEncryption_oid =
634             ObjectIdentifier.of(KnownOIDs.RSA);
635 
636     public static final ObjectIdentifier RSASSA_PSS_oid =
637             ObjectIdentifier.of(KnownOIDs.RSASSA_PSS);
638 
639     public static final ObjectIdentifier MGF1_oid =
640             ObjectIdentifier.of(KnownOIDs.MGF1);
641 
642     public static final ObjectIdentifier ed25519_oid =
643             ObjectIdentifier.of(KnownOIDs.Ed25519);
644     public static final ObjectIdentifier ed448_oid =
645             ObjectIdentifier.of(KnownOIDs.Ed448);
646 
647     /**
648      * Creates a signature algorithm name from a digest algorithm
649      * name and a encryption algorithm name.
650      */
651     public static String makeSigAlg(String digAlg, String encAlg) {
652         digAlg = digAlg.replace("-", "");
653         if (encAlg.equalsIgnoreCase("EC")) encAlg = "ECDSA";
654 
655         return digAlg + "with" + encAlg;
656     }
657 
658     /**
659      * Extracts the encryption algorithm name from a signature
660      * algorithm name.
661      */
662     public static String getEncAlgFromSigAlg(String signatureAlgorithm) {
663         signatureAlgorithm = signatureAlgorithm.toUpperCase(Locale.ENGLISH);
664         int with = signatureAlgorithm.indexOf("WITH");
665         String keyAlgorithm = null;
666         if (with > 0) {
667             int and = signatureAlgorithm.indexOf("AND", with + 4);
668             if (and > 0) {
669                 keyAlgorithm = signatureAlgorithm.substring(with + 4, and);
670             } else {
671                 keyAlgorithm = signatureAlgorithm.substring(with + 4);
672             }
673             if (keyAlgorithm.equalsIgnoreCase("ECDSA")) {
674                 keyAlgorithm = "EC";
675             }
676         }
677         return keyAlgorithm;
678     }
679 
680     /**
681      * Extracts the digest algorithm name from a signature
682      * algorithm name.
683      */
684     public static String getDigAlgFromSigAlg(String signatureAlgorithm) {
685         signatureAlgorithm = signatureAlgorithm.toUpperCase(Locale.ENGLISH);
686         int with = signatureAlgorithm.indexOf("WITH");
687         if (with > 0) {
688             return signatureAlgorithm.substring(0, with);
689         }
690         return null;
691     }
692 
693     /**
694      * Checks if a signature algorithm matches a key algorithm, i.e. a
695      * signature can be initialized with a key.
696      *
697      * @param kAlg must not be null
698      * @param sAlg must not be null
699      * @throws IllegalArgumentException if they do not match
700      */
701     public static void checkKeyAndSigAlgMatch(String kAlg, String sAlg) {
702         String sAlgUp = sAlg.toUpperCase(Locale.US);
703         if ((sAlgUp.endsWith("WITHRSA") && !kAlg.equalsIgnoreCase("RSA")) ||
704                 (sAlgUp.endsWith("WITHECDSA") && !kAlg.equalsIgnoreCase("EC")) ||
705                 (sAlgUp.endsWith("WITHDSA") && !kAlg.equalsIgnoreCase("DSA"))) {
706             throw new IllegalArgumentException(
707                     "key algorithm not compatible with signature algorithm");
708         }
709     }
710 
711     /**
712      * Returns the default signature algorithm for a private key. The digest
713      * part might evolve with time. Remember to update the spec of
714      * {@link jdk.security.jarsigner.JarSigner.Builder#getDefaultSignatureAlgorithm(PrivateKey)}
715      * if updated.
716      *
717      * @param k cannot be null
718      * @return the default alg, might be null if unsupported
719      */
720     public static String getDefaultSigAlgForKey(PrivateKey k) {
721         switch (k.getAlgorithm().toUpperCase(Locale.ENGLISH)) {
722             case "EC":
723                 return ecStrength(KeyUtil.getKeySize(k))
724                     + "withECDSA";
725             case "DSA":
726                 return ifcFfcStrength(KeyUtil.getKeySize(k))
727                     + "withDSA";
728             case "RSA":
729                 return ifcFfcStrength(KeyUtil.getKeySize(k))
730                     + "withRSA";
731             case "RSASSA-PSS":
732                 return "RSASSA-PSS";
733             case "EDDSA":
734                 return edAlgFromKey(k);
735             default:
736                 return null;
737         }
738     }
739 
740     // Most commonly used PSSParameterSpec and AlgorithmId
741     private static class PSSParamsHolder {
742 
743         final static PSSParameterSpec PSS_256_SPEC = new PSSParameterSpec(
744                 "SHA-256", "MGF1",
745                 new MGF1ParameterSpec("SHA-256"),
746                 32, PSSParameterSpec.TRAILER_FIELD_BC);
747         final static PSSParameterSpec PSS_384_SPEC = new PSSParameterSpec(
748                 "SHA-384", "MGF1",
749                 new MGF1ParameterSpec("SHA-384"),
750                 48, PSSParameterSpec.TRAILER_FIELD_BC);
751         final static PSSParameterSpec PSS_512_SPEC = new PSSParameterSpec(
752                 "SHA-512", "MGF1",
753                 new MGF1ParameterSpec("SHA-512"),
754                 64, PSSParameterSpec.TRAILER_FIELD_BC);
755 
756         final static AlgorithmId PSS_256_ID;
757         final static AlgorithmId PSS_384_ID;
758         final static AlgorithmId PSS_512_ID;
759 
760         static {
761             try {
762                 PSS_256_ID = new AlgorithmId(RSASSA_PSS_oid,
763                         new DerValue(PSSParameters.getEncoded(PSS_256_SPEC)));
764                 PSS_384_ID = new AlgorithmId(RSASSA_PSS_oid,
765                         new DerValue(PSSParameters.getEncoded(PSS_384_SPEC)));
766                 PSS_512_ID = new AlgorithmId(RSASSA_PSS_oid,
767                         new DerValue(PSSParameters.getEncoded(PSS_512_SPEC)));
768             } catch (IOException e) {
769                 throw new AssertionError("Should not happen", e);
770             }
771         }
772     }
773 
774     public static AlgorithmId getWithParameterSpec(String algName,
775             AlgorithmParameterSpec spec) throws NoSuchAlgorithmException {
776 
777         if (spec == null) {
778             return AlgorithmId.get(algName);
779         } else if (spec == PSSParamsHolder.PSS_256_SPEC) {
780             return PSSParamsHolder.PSS_256_ID;
781         } else if (spec == PSSParamsHolder.PSS_384_SPEC) {
782             return PSSParamsHolder.PSS_384_ID;
783         } else if (spec == PSSParamsHolder.PSS_512_SPEC) {
784             return PSSParamsHolder.PSS_512_ID;
785         } else if (spec instanceof EdDSAParameterSpec) {
786             return AlgorithmId.get(algName);
787         } else {
788             try {
789                 AlgorithmParameters result =
790                         AlgorithmParameters.getInstance(algName);
791                 result.init(spec);
792                 return get(result);
793             } catch (InvalidParameterSpecException | NoSuchAlgorithmException e) {
794                 throw new ProviderException(e);
795             }
796         }
797     }
798 
799     public static AlgorithmParameterSpec getDefaultAlgorithmParameterSpec(
800             String sigAlg, PrivateKey k) {
801         if (sigAlg.equalsIgnoreCase("RSASSA-PSS")) {
802             if (k instanceof RSAKey) {
803                 AlgorithmParameterSpec spec = ((RSAKey) k).getParams();
804                 if (spec instanceof PSSParameterSpec) {
805                     return spec;
806                 }
807             }
808             switch (ifcFfcStrength(KeyUtil.getKeySize(k))) {
809                 case "SHA256":
810                     return PSSParamsHolder.PSS_256_SPEC;
811                 case "SHA384":
812                     return PSSParamsHolder.PSS_384_SPEC;
813                 case "SHA512":
814                     return PSSParamsHolder.PSS_512_SPEC;
815                 default:
816                     throw new AssertionError("Should not happen");
817             }
818         } else {
819             return null;
820         }
821     }
822 
823     private static String edAlgFromKey(PrivateKey k) {
824         if (k instanceof EdECPrivateKey) {
825             EdECPrivateKey edKey = (EdECPrivateKey) k;
826             return edKey.getParams().getName();
827         }
828         return "EdDSA";
829     }
830 
831     // Values from SP800-57 part 1 rev 4 tables 2 and 3
832     private static String ecStrength (int bitLength) {
833         if (bitLength >= 512) { // 256 bits of strength
834             return "SHA512";
835         } else if (bitLength >= 384) {  // 192 bits of strength
836             return "SHA384";
837         } else { // 128 bits of strength and less
838             return "SHA256";
839         }
840     }
841 
842     // Same values for RSA and DSA
843     private static String ifcFfcStrength (int bitLength) {
844         if (bitLength > 7680) { // 256 bits
845             return "SHA512";
846         } else if (bitLength > 3072) {  // 192 bits
847             return "SHA384";
848         } else  { // 128 bits and less
849             return "SHA256";
850         }
851     }
852 }