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)DSA_oid) ||
200                 algid.equals((Object)sha1WithDSA_oid)) {
201                 ; // no parameter part encoded
202             } else {
203                 bytes.putNull();
204             }*/
205             if (algid.equals(RSASSA_PSS_oid) || algid.equals(ed448_oid)
206                     || algid.equals(ed25519_oid)) {
207                 // RFC 4055 3.3: when an RSASSA-PSS key does not require
208                 // parameter validation, field is absent.
209             } else {
210                 bytes.putNull();
211             }
212         } else {
213             bytes.putDerValue(params);
214         }
215         tmp.write(DerValue.tag_Sequence, bytes);
216         out.write(tmp.toByteArray());
217     }
218 
219 
220     /**
221      * Returns the DER-encoded X.509 AlgorithmId as a byte array.
222      */
223     public final byte[] encode() throws IOException {
224         DerOutputStream out = new DerOutputStream();
225         derEncode(out);
226         return out.toByteArray();
227     }
228 
229     /**
230      * Returns the ISO OID for this algorithm.  This is usually converted
231      * to a string and used as part of an algorithm name, for example
232      * "OID.1.3.14.3.2.13" style notation.  Use the <code>getName</code>
233      * call when you do not need to ensure cross-system portability
234      * of algorithm names, or need a user friendly name.
235      */
236     public final ObjectIdentifier getOID () {
237         return algid;
238     }
239 
240     /**
241      * Returns a name for the algorithm which may be more intelligible
242      * to humans than the algorithm's OID, but which won't necessarily
243      * be comprehensible on other systems.  For example, this might
244      * return a name such as "MD5withRSA" for a signature algorithm on
245      * some systems.  It also returns names like "OID.1.2.3.4", when
246      * no particular name for the algorithm is known.
247      *
248      * Note: for ecdsa-with-SHA2 plus hash algorithm (Ex: SHA-256), this method
249      * returns the "full" signature algorithm (Ex: SHA256withECDSA) directly.
250      */
251     public String getName() {
252         String oidStr = algid.toString();
253         // first check the list of support oids
254         KnownOIDs o = KnownOIDs.findMatch(oidStr);
255         if (o == KnownOIDs.SpecifiedSHA2withECDSA) {
256             if (params != null) {
257                 try {
258                     AlgorithmId paramsId =
259                         AlgorithmId.parse(new DerValue(params.toByteArray()));
260                     String paramsName = paramsId.getName();
261                     return makeSigAlg(paramsName, "EC");
262                 } catch (IOException e) {
263                     // ignore
264                 }
265             }
266         }
267         if (o != null) {
268             return o.stdName();
269         } else {
270             String n = aliasOidsTable().get(oidStr);
271             if (n != null) {
272                 return n;
273             } else {
274                 return algid.toString();
275             }
276         }
277     }
278 
279     public AlgorithmParameters getParameters() {
280         return algParams;
281     }
282 
283     /**
284      * Returns the DER encoded parameter, which can then be
285      * used to initialize java.security.AlgorithmParameters.
286      *
287      * Note: for ecdsa-with-SHA2 plus hash algorithm (Ex: SHA-256), this method
288      * returns null because {@link #getName()} has already returned the "full"
289      * signature algorithm (Ex: SHA256withECDSA).
290      *
291      * @return DER encoded parameters, or null not present.
292      */
293     public byte[] getEncodedParams() throws IOException {
294         return (params == null ||
295             algid.toString().equals(KnownOIDs.SpecifiedSHA2withECDSA.value()))
296                 ? null
297                 : params.toByteArray();
298     }
299 
300     /**
301      * Returns true iff the argument indicates the same algorithm
302      * with the same parameters.
303      */
304     public boolean equals(AlgorithmId other) {
305         boolean paramsEqual = Objects.equals(other.params, params);
306         return (algid.equals((Object)other.algid) && paramsEqual);
307     }
308 
309     /**
310      * Compares this AlgorithmID to another.  If algorithm parameters are
311      * available, they are compared.  Otherwise, just the object IDs
312      * for the algorithm are compared.
313      *
314      * @param other preferably an AlgorithmId, else an ObjectIdentifier
315      */
316     public boolean equals(Object other) {
317         if (this == other) {
318             return true;
319         }
320         if (other instanceof AlgorithmId) {
321             return equals((AlgorithmId) other);
322         } else if (other instanceof ObjectIdentifier) {
323             return equals((ObjectIdentifier) other);
324         } else {
325             return false;
326         }
327     }
328 
329     /**
330      * Compares two algorithm IDs for equality.  Returns true iff
331      * they are the same algorithm, ignoring algorithm parameters.
332      */
333     public final boolean equals(ObjectIdentifier id) {
334         return algid.equals((Object)id);
335     }
336 
337     /**
338      * Returns a hashcode for this AlgorithmId.
339      *
340      * @return a hashcode for this AlgorithmId.
341      */
342     public int hashCode() {
343         StringBuilder sbuf = new StringBuilder();
344         sbuf.append(algid.toString());
345         sbuf.append(paramsToString());
346         return sbuf.toString().hashCode();
347     }
348 
349     /**
350      * Provides a human-readable description of the algorithm parameters.
351      * This may be redefined by subclasses which parse those parameters.
352      */
353     protected String paramsToString() {
354         if (params == null) {
355             return "";
356         } else if (algParams != null) {
357             return algParams.toString();
358         } else {
359             return ", params unparsed";
360         }
361     }
362 
363     /**
364      * Returns a string describing the algorithm and its parameters.
365      */
366     public String toString() {
367         return getName() + paramsToString();
368     }
369 
370     /**
371      * Parse (unmarshal) an ID from a DER sequence input value.  This form
372      * parsing might be used when expanding a value which has already been
373      * partially unmarshaled as a set or sequence member.
374      *
375      * @exception IOException on error.
376      * @param val the input value, which contains the algid and, if
377      *          there are any parameters, those parameters.
378      * @return an ID for the algorithm.  If the system is configured
379      *          appropriately, this may be an instance of a class
380      *          with some kind of special support for this algorithm.
381      *          In that case, you may "narrow" the type of the ID.
382      */
383     public static AlgorithmId parse(DerValue val) throws IOException {
384         if (val.tag != DerValue.tag_Sequence) {
385             throw new IOException("algid parse error, not a sequence");
386         }
387 
388         /*
389          * Get the algorithm ID and any parameters.
390          */
391         ObjectIdentifier        algid;
392         DerValue                params;
393         DerInputStream          in = val.toDerInputStream();
394 
395         algid = in.getOID();
396         if (in.available() == 0) {
397             params = null;
398         } else {
399             params = in.getDerValue();
400             if (params.tag == DerValue.tag_Null) {
401                 if (params.length() != 0) {
402                     throw new IOException("invalid NULL");
403                 }
404                 params = null;
405             }
406             if (in.available() != 0) {
407                 throw new IOException("Invalid AlgorithmIdentifier: extra data");
408             }
409         }
410 
411         return new AlgorithmId(algid, params);
412     }
413 
414     /**
415      * Returns one of the algorithm IDs most commonly associated
416      * with this algorithm name.
417      *
418      * @param algname the name being used
419      * @deprecated use the short get form of this method.
420      * @exception NoSuchAlgorithmException on error.
421      */
422     @Deprecated
423     public static AlgorithmId getAlgorithmId(String algname)
424             throws NoSuchAlgorithmException {
425         return get(algname);
426     }
427 
428     /**
429      * Returns one of the algorithm IDs most commonly associated
430      * with this algorithm name.
431      *
432      * @param algname the name being used
433      * @exception NoSuchAlgorithmException on error.
434      */
435     public static AlgorithmId get(String algname)
436             throws NoSuchAlgorithmException {
437         ObjectIdentifier oid;
438         try {
439             oid = algOID(algname);
440         } catch (IOException ioe) {
441             throw new NoSuchAlgorithmException
442                 ("Invalid ObjectIdentifier " + algname);
443         }
444 
445         if (oid == null) {
446             throw new NoSuchAlgorithmException
447                 ("unrecognized algorithm name: " + algname);
448         }
449         return new AlgorithmId(oid);
450     }
451 
452     /**
453      * Returns one of the algorithm IDs most commonly associated
454      * with this algorithm parameters.
455      *
456      * @param algparams the associated algorithm parameters.
457      * @exception NoSuchAlgorithmException on error.
458      */
459     public static AlgorithmId get(AlgorithmParameters algparams)
460             throws NoSuchAlgorithmException {
461         ObjectIdentifier oid;
462         String algname = algparams.getAlgorithm();
463         try {
464             oid = algOID(algname);
465         } catch (IOException ioe) {
466             throw new NoSuchAlgorithmException
467                 ("Invalid ObjectIdentifier " + algname);
468         }
469         if (oid == null) {
470             throw new NoSuchAlgorithmException
471                 ("unrecognized algorithm name: " + algname);
472         }
473         return new AlgorithmId(oid, algparams);
474     }
475 
476     /*
477      * Translates from some common algorithm names to the
478      * OID with which they're usually associated ... this mapping
479      * is the reverse of the one below, except in those cases
480      * where synonyms are supported or where a given algorithm
481      * is commonly associated with multiple OIDs.
482      *
483      * XXX This method needs to be enhanced so that we can also pass the
484      * scope of the algorithm name to it, e.g., the algorithm name "DSA"
485      * may have a different OID when used as a "Signature" algorithm than when
486      * used as a "KeyPairGenerator" algorithm.
487      */
488     private static ObjectIdentifier algOID(String name) throws IOException {
489         if (name.startsWith("OID.")) {
490             name = name.substring("OID.".length());
491         }
492 
493         KnownOIDs k = KnownOIDs.findMatch(name);
494         if (k != null) {
495             return ObjectIdentifier.of(k);
496         }
497 
498         // unknown algorithm oids
499         if (name.indexOf(".") == -1) {
500             // see if there is a matching oid string alias mapping from
501             // 3rd party providers
502             name = name.toUpperCase(Locale.ENGLISH);
503             String oidStr = aliasOidsTable().get(name);
504             if (oidStr != null) {
505                 return ObjectIdentifier.of(oidStr);
506             } return null;
507         } else {
508             return ObjectIdentifier.of(name);
509         }
510     }
511 
512     // oid string cache index'ed by algorithm name and oid strings
513     private static volatile Map<String,String> aliasOidsTable;
514 
515     // returns the aliasOidsTable, lazily initializing it on first access.
516     private static Map<String,String> aliasOidsTable() {
517         // Double checked locking; safe because aliasOidsTable is volatile
518         Map<String,String> tab = aliasOidsTable;
519         if (tab == null) {
520             synchronized (AlgorithmId.class) {
521                 if ((tab = aliasOidsTable) == null) {
522                     aliasOidsTable = tab = collectOIDAliases();
523                 }
524             }
525         }
526         return tab;
527     }
528 
529     private static boolean isKnownProvider(Provider p) {
530         String pn = p.getName();
531         String mn = p.getClass().getModule().getName();
532         if (pn != null && mn != null) {
533             return ((mn.equals("java.base") &&
534                     (pn.equals("SUN") || pn.equals("SunRsaSign") ||
535                     pn.equals("SunJCE") || pn.equals("SunJSSE"))) ||
536                 (mn.equals("jdk.crypto.ec") && pn.equals("SunEC")) ||
537                 (mn.equals("jdk.crypto.mscapi") && pn.equals("SunMSCAPI")) ||
538                 (mn.equals("jdk.crypto.cryptoki") &&
539                     pn.startsWith("SunPKCS11")));
540         } else {
541             return false;
542         }
543     }
544 
545     private static ConcurrentHashMap<String, String> collectOIDAliases() {
546         ConcurrentHashMap<String, String> t = new ConcurrentHashMap<>();
547         for (Provider provider : Security.getProviders()) {
548             // skip providers which are already using SecurityProviderConstants
549             // and KnownOIDs
550             if (isKnownProvider(provider)) {
551                 continue;
552             }
553             for (Object key : provider.keySet()) {
554                 String alias = (String)key;
555                 String upperCaseAlias = alias.toUpperCase(Locale.ENGLISH);
556                 int index;
557                 if (upperCaseAlias.startsWith("ALG.ALIAS") &&
558                     (index = upperCaseAlias.indexOf("OID.", 0)) != -1) {
559                     index += "OID.".length();
560                     if (index == alias.length()) {
561                         // invalid alias entry
562                         break;
563                     }
564                     String ostr = alias.substring(index);
565                     String stdAlgName = provider.getProperty(alias);
566                     if (stdAlgName != null) {
567                         stdAlgName = stdAlgName.toUpperCase(Locale.ENGLISH);
568                     }
569                     // add the name->oid and oid->name mappings if none exists
570                     if (KnownOIDs.findMatch(stdAlgName) == null) {
571                         // not override earlier entries if it exists
572                         t.putIfAbsent(stdAlgName, ostr);
573                     }
574                     if (KnownOIDs.findMatch(ostr) == null) {
575                         // not override earlier entries if it exists
576                         t.putIfAbsent(ostr, stdAlgName);
577                     }
578                 }
579             }
580         }
581         return t;
582     }
583 
584     public static final ObjectIdentifier MD2_oid =
585             ObjectIdentifier.of(KnownOIDs.MD2);
586 
587     public static final ObjectIdentifier MD5_oid =
588             ObjectIdentifier.of(KnownOIDs.MD5);
589 
590     public static final ObjectIdentifier SHA_oid =
591             ObjectIdentifier.of(KnownOIDs.SHA_1);
592 
593     public static final ObjectIdentifier SHA224_oid =
594             ObjectIdentifier.of(KnownOIDs.SHA_224);
595 
596     public static final ObjectIdentifier SHA256_oid =
597             ObjectIdentifier.of(KnownOIDs.SHA_256);
598 
599     public static final ObjectIdentifier SHA384_oid =
600             ObjectIdentifier.of(KnownOIDs.SHA_384);
601 
602     public static final ObjectIdentifier SHA512_oid =
603             ObjectIdentifier.of(KnownOIDs.SHA_512);
604 
605     public static final ObjectIdentifier SHA512_224_oid =
606             ObjectIdentifier.of(KnownOIDs.SHA_512$224);
607 
608     public static final ObjectIdentifier SHA512_256_oid =
609             ObjectIdentifier.of(KnownOIDs.SHA_512$256);
610 
611     public static final ObjectIdentifier DSA_oid =
612             ObjectIdentifier.of(KnownOIDs.DSA);
613 
614     public static final ObjectIdentifier EC_oid =
615             ObjectIdentifier.of(KnownOIDs.EC);
616 
617     public static final ObjectIdentifier RSAEncryption_oid =
618             ObjectIdentifier.of(KnownOIDs.RSA);
619 
620     public static final ObjectIdentifier RSASSA_PSS_oid =
621             ObjectIdentifier.of(KnownOIDs.RSASSA_PSS);
622 
623     public static final ObjectIdentifier MGF1_oid =
624             ObjectIdentifier.of(KnownOIDs.MGF1);
625 
626     public static final ObjectIdentifier ed25519_oid =
627             ObjectIdentifier.of(KnownOIDs.Ed25519);
628     public static final ObjectIdentifier ed448_oid =
629             ObjectIdentifier.of(KnownOIDs.Ed448);
630 
631     /**
632      * Creates a signature algorithm name from a digest algorithm
633      * name and a encryption algorithm name.
634      */
635     public static String makeSigAlg(String digAlg, String encAlg) {
636         digAlg = digAlg.replace("-", "");
637         if (encAlg.equalsIgnoreCase("EC")) encAlg = "ECDSA";
638 
639         return digAlg + "with" + encAlg;
640     }
641 
642     /**
643      * Extracts the encryption algorithm name from a signature
644      * algorithm name.
645      */
646     public static String getEncAlgFromSigAlg(String signatureAlgorithm) {
647         signatureAlgorithm = signatureAlgorithm.toUpperCase(Locale.ENGLISH);
648         int with = signatureAlgorithm.indexOf("WITH");
649         String keyAlgorithm = null;
650         if (with > 0) {
651             int and = signatureAlgorithm.indexOf("AND", with + 4);
652             if (and > 0) {
653                 keyAlgorithm = signatureAlgorithm.substring(with + 4, and);
654             } else {
655                 keyAlgorithm = signatureAlgorithm.substring(with + 4);
656             }
657             if (keyAlgorithm.equalsIgnoreCase("ECDSA")) {
658                 keyAlgorithm = "EC";
659             }
660         }
661         return keyAlgorithm;
662     }
663 
664     /**
665      * Extracts the digest algorithm name from a signature
666      * algorithm name.
667      */
668     public static String getDigAlgFromSigAlg(String signatureAlgorithm) {
669         signatureAlgorithm = signatureAlgorithm.toUpperCase(Locale.ENGLISH);
670         int with = signatureAlgorithm.indexOf("WITH");
671         if (with > 0) {
672             return signatureAlgorithm.substring(0, with);
673         }
674         return null;
675     }
676 
677     /**
678      * Checks if a signature algorithm matches a key algorithm, i.e. a
679      * signature can be initialized with a key.
680      *
681      * @param kAlg must not be null
682      * @param sAlg must not be null
683      * @throws IllegalArgumentException if they do not match
684      */
685     public static void checkKeyAndSigAlgMatch(String kAlg, String sAlg) {
686         String sAlgUp = sAlg.toUpperCase(Locale.US);
687         if ((sAlgUp.endsWith("WITHRSA") && !kAlg.equalsIgnoreCase("RSA")) ||
688                 (sAlgUp.endsWith("WITHECDSA") && !kAlg.equalsIgnoreCase("EC")) ||
689                 (sAlgUp.endsWith("WITHDSA") && !kAlg.equalsIgnoreCase("DSA"))) {
690             throw new IllegalArgumentException(
691                     "key algorithm not compatible with signature algorithm");
692         }
693     }
694 
695     /**
696      * Returns the default signature algorithm for a private key. The digest
697      * part might evolve with time. Remember to update the spec of
698      * {@link jdk.security.jarsigner.JarSigner.Builder#getDefaultSignatureAlgorithm(PrivateKey)}
699      * if updated.
700      *
701      * @param k cannot be null
702      * @return the default alg, might be null if unsupported
703      */
704     public static String getDefaultSigAlgForKey(PrivateKey k) {
705         switch (k.getAlgorithm().toUpperCase(Locale.ENGLISH)) {
706             case "EC":
707                 return ecStrength(KeyUtil.getKeySize(k))
708                     + "withECDSA";
709             case "DSA":
710                 return ifcFfcStrength(KeyUtil.getKeySize(k))
711                     + "withDSA";
712             case "RSA":
713                 return ifcFfcStrength(KeyUtil.getKeySize(k))
714                     + "withRSA";
715             case "RSASSA-PSS":
716                 return "RSASSA-PSS";
717             case "EDDSA":
718                 return edAlgFromKey(k);
719             default:
720                 return null;
721         }
722     }
723 
724     // Most commonly used PSSParameterSpec and AlgorithmId
725     private static class PSSParamsHolder {
726 
727         final static PSSParameterSpec PSS_256_SPEC = new PSSParameterSpec(
728                 "SHA-256", "MGF1",
729                 new MGF1ParameterSpec("SHA-256"),
730                 32, PSSParameterSpec.TRAILER_FIELD_BC);
731         final static PSSParameterSpec PSS_384_SPEC = new PSSParameterSpec(
732                 "SHA-384", "MGF1",
733                 new MGF1ParameterSpec("SHA-384"),
734                 48, PSSParameterSpec.TRAILER_FIELD_BC);
735         final static PSSParameterSpec PSS_512_SPEC = new PSSParameterSpec(
736                 "SHA-512", "MGF1",
737                 new MGF1ParameterSpec("SHA-512"),
738                 64, PSSParameterSpec.TRAILER_FIELD_BC);
739 
740         final static AlgorithmId PSS_256_ID;
741         final static AlgorithmId PSS_384_ID;
742         final static AlgorithmId PSS_512_ID;
743 
744         static {
745             try {
746                 PSS_256_ID = new AlgorithmId(RSASSA_PSS_oid,
747                         new DerValue(PSSParameters.getEncoded(PSS_256_SPEC)));
748                 PSS_384_ID = new AlgorithmId(RSASSA_PSS_oid,
749                         new DerValue(PSSParameters.getEncoded(PSS_384_SPEC)));
750                 PSS_512_ID = new AlgorithmId(RSASSA_PSS_oid,
751                         new DerValue(PSSParameters.getEncoded(PSS_512_SPEC)));
752             } catch (IOException e) {
753                 throw new AssertionError("Should not happen", e);
754             }
755         }
756     }
757 
758     public static AlgorithmId getWithParameterSpec(String algName,
759             AlgorithmParameterSpec spec) throws NoSuchAlgorithmException {
760 
761         if (spec == null) {
762             return AlgorithmId.get(algName);
763         } else if (spec == PSSParamsHolder.PSS_256_SPEC) {
764             return PSSParamsHolder.PSS_256_ID;
765         } else if (spec == PSSParamsHolder.PSS_384_SPEC) {
766             return PSSParamsHolder.PSS_384_ID;
767         } else if (spec == PSSParamsHolder.PSS_512_SPEC) {
768             return PSSParamsHolder.PSS_512_ID;
769         } else if (spec instanceof EdDSAParameterSpec) {
770             return AlgorithmId.get(algName);
771         } else {
772             try {
773                 AlgorithmParameters result =
774                         AlgorithmParameters.getInstance(algName);
775                 result.init(spec);
776                 return get(result);
777             } catch (InvalidParameterSpecException | NoSuchAlgorithmException e) {
778                 throw new ProviderException(e);
779             }
780         }
781     }
782 
783     public static AlgorithmParameterSpec getDefaultAlgorithmParameterSpec(
784             String sigAlg, PrivateKey k) {
785         if (sigAlg.equalsIgnoreCase("RSASSA-PSS")) {
786             if (k instanceof RSAKey) {
787                 AlgorithmParameterSpec spec = ((RSAKey) k).getParams();
788                 if (spec instanceof PSSParameterSpec) {
789                     return spec;
790                 }
791             }
792             switch (ifcFfcStrength(KeyUtil.getKeySize(k))) {
793                 case "SHA256":
794                     return PSSParamsHolder.PSS_256_SPEC;
795                 case "SHA384":
796                     return PSSParamsHolder.PSS_384_SPEC;
797                 case "SHA512":
798                     return PSSParamsHolder.PSS_512_SPEC;
799                 default:
800                     throw new AssertionError("Should not happen");
801             }
802         } else {
803             return null;
804         }
805     }
806 
807     private static String edAlgFromKey(PrivateKey k) {
808         if (k instanceof EdECPrivateKey) {
809             EdECPrivateKey edKey = (EdECPrivateKey) k;
810             return edKey.getParams().getName();
811         }
812         return "EdDSA";
813     }
814 
815     // Values from SP800-57 part 1 rev 4 tables 2 and 3
816     private static String ecStrength (int bitLength) {
817         if (bitLength >= 512) { // 256 bits of strength
818             return "SHA512";
819         } else if (bitLength >= 384) {  // 192 bits of strength
820             return "SHA384";
821         } else { // 128 bits of strength and less
822             return "SHA256";
823         }
824     }
825 
826     // Same values for RSA and DSA
827     private static String ifcFfcStrength (int bitLength) {
828         if (bitLength > 7680) { // 256 bits
829             return "SHA512";
830         } else if (bitLength > 3072) {  // 192 bits
831             return "SHA384";
832         } else  { // 128 bits and less
833             return "SHA256";
834         }
835     }
836 }