Path: blob/master/src/java.base/share/classes/sun/security/x509/AlgorithmId.java
41159 views
/*1* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.x509;2627import java.io.*;28import java.util.*;29import java.util.concurrent.ConcurrentHashMap;30import java.security.*;3132import sun.security.util.*;333435/**36* This class identifies algorithms, such as cryptographic transforms, each37* of which may be associated with parameters. Instances of this base class38* are used when this runtime environment has no special knowledge of the39* algorithm type, and may also be used in other cases. Equivalence is40* defined according to OID and (where relevant) parameters.41*42* <P>Subclasses may be used, for example when the algorithm ID has43* associated parameters which some code (e.g. code using public keys) needs44* to have parsed. Two examples of such algorithms are Diffie-Hellman key45* exchange, and the Digital Signature Standard Algorithm (DSS/DSA).46*47* <P>The OID constants defined in this class correspond to some widely48* used algorithms, for which conventional string names have been defined.49* This class is not a general repository for OIDs, or for such string names.50* Note that the mappings between algorithm IDs and algorithm names is51* not one-to-one.52*53*54* @author David Brownell55* @author Amit Kapoor56* @author Hemma Prafullchandra57*/58public class AlgorithmId implements Serializable, DerEncoder {5960/** use serialVersionUID from JDK 1.1. for interoperability */61@java.io.Serial62private static final long serialVersionUID = 7205873507486557157L;6364/**65* The object identitifer being used for this algorithm.66*/67private ObjectIdentifier algid;6869// The (parsed) parameters70@SuppressWarnings("serial") // Not statically typed as Serializable71private AlgorithmParameters algParams;7273/**74* Parameters for this algorithm. These are stored in unparsed75* DER-encoded form; subclasses can be made to automaticaly parse76* them so there is fast access to these parameters.77*/78protected transient byte[] encodedParams;7980/**81* Constructs an algorithm ID which will be initialized82* separately, for example by deserialization.83* @deprecated use one of the other constructors.84*/85@Deprecated86public AlgorithmId() { }8788/**89* Constructs a parameterless algorithm ID.90*91* @param oid the identifier for the algorithm92*/93public AlgorithmId(ObjectIdentifier oid) {94algid = oid;95}9697/**98* Constructs an algorithm ID with algorithm parameters.99*100* @param oid the identifier for the algorithm.101* @param algparams the associated algorithm parameters, can be null.102*/103public AlgorithmId(ObjectIdentifier oid, AlgorithmParameters algparams) {104algid = oid;105this.algParams = algparams;106if (algParams != null) {107try {108encodedParams = algParams.getEncoded();109} catch (IOException ioe) {110// Ignore this at the moment. This exception can occur111// if AlgorithmParameters was not initialized yet. Will112// try to re-getEncoded() again later.113}114}115}116117/**118* Constructs an algorithm ID with algorithm parameters as a DerValue.119*120* @param oid the identifier for the algorithm.121* @param params the associated algorithm parameters, can be null.122*/123public AlgorithmId(ObjectIdentifier oid, DerValue params)124throws IOException {125this.algid = oid;126if (params != null) {127encodedParams = params.toByteArray();128decodeParams();129}130}131132protected void decodeParams() throws IOException {133String algidName = getName();134try {135algParams = AlgorithmParameters.getInstance(algidName);136} catch (NoSuchAlgorithmException e) {137/*138* This algorithm parameter type is not supported, so we cannot139* parse the parameters.140*/141algParams = null;142return;143}144145// Decode (parse) the parameters146algParams.init(encodedParams.clone());147}148149/**150* Marshal a DER-encoded "AlgorithmID" sequence on the DER stream.151*/152public final void encode(DerOutputStream out) throws IOException {153derEncode(out);154}155156/**157* DER encode this object onto an output stream.158* Implements the <code>DerEncoder</code> interface.159*160* @param out161* the output stream on which to write the DER encoding.162*163* @exception IOException on encoding error.164*/165@Override166public void derEncode (OutputStream out) throws IOException {167DerOutputStream bytes = new DerOutputStream();168DerOutputStream tmp = new DerOutputStream();169170bytes.putOID(algid);171172// Re-getEncoded() from algParams if it was not initialized173if (algParams != null && encodedParams == null) {174encodedParams = algParams.getEncoded();175// If still not initialized. Let the IOE be thrown.176}177178if (encodedParams == null) {179// Changes backed out for compatibility with Solaris180181// Several AlgorithmId should omit the whole parameter part when182// it's NULL. They are ---183// RFC 3370 2.1: Implementations SHOULD generate SHA-1184// AlgorithmIdentifiers with absent parameters.185// RFC 3447 C1: When id-sha1, id-sha224, id-sha256, id-sha384 and186// id-sha512 are used in an AlgorithmIdentifier the parameters187// (which are optional) SHOULD be omitted.188// RFC 3279 2.3.2: The id-dsa algorithm syntax includes optional189// domain parameters... When omitted, the parameters component190// MUST be omitted entirely191// RFC 3370 3.1: When the id-dsa-with-sha1 algorithm identifier192// is used, the AlgorithmIdentifier parameters field MUST be absent.193/*if (194algid.equals((Object)SHA_oid) ||195algid.equals((Object)SHA224_oid) ||196algid.equals((Object)SHA256_oid) ||197algid.equals((Object)SHA384_oid) ||198algid.equals((Object)SHA512_oid) ||199algid.equals((Object)SHA512_224_oid) ||200algid.equals((Object)SHA512_256_oid) ||201algid.equals((Object)SHA3_224_oid) ||202algid.equals((Object)SHA3_256_oid) ||203algid.equals((Object)SHA3_384_oid) ||204algid.equals((Object)SHA3_512_oid) ||205algid.equals((Object)DSA_oid) ||206algid.equals((Object)sha1WithDSA_oid)) {207; // no parameter part encoded208} else {209bytes.putNull();210}*/211if (algid.equals(RSASSA_PSS_oid) || algid.equals(ed448_oid)212|| algid.equals(ed25519_oid)213|| algid.equals(x448_oid)214|| algid.equals(x25519_oid)215|| algid.equals(SHA224withECDSA_oid)216|| algid.equals(SHA256withECDSA_oid)217|| algid.equals(SHA384withECDSA_oid)218|| algid.equals(SHA512withECDSA_oid)) {219// RFC 4055 3.3: when an RSASSA-PSS key does not require220// parameter validation, field is absent.221// RFC 8410 3: for id-X25519, id-X448, id-Ed25519, and222// id-Ed448, the parameters must be absent.223// RFC 5758 3.2: the encoding must omit the parameters field224// for ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-SHA384225// and ecdsa-with-SHA512.226} else {227bytes.putNull();228}229} else {230bytes.write(encodedParams);231}232tmp.write(DerValue.tag_Sequence, bytes);233out.write(tmp.toByteArray());234}235236237/**238* Returns the DER-encoded X.509 AlgorithmId as a byte array.239*/240public final byte[] encode() throws IOException {241DerOutputStream out = new DerOutputStream();242derEncode(out);243return out.toByteArray();244}245246/**247* Returns the ISO OID for this algorithm. This is usually converted248* to a string and used as part of an algorithm name, for example249* "OID.1.3.14.3.2.13" style notation. Use the <code>getName</code>250* call when you do not need to ensure cross-system portability251* of algorithm names, or need a user friendly name.252*/253public final ObjectIdentifier getOID () {254return algid;255}256257/**258* Returns a name for the algorithm which may be more intelligible259* to humans than the algorithm's OID, but which won't necessarily260* be comprehensible on other systems. For example, this might261* return a name such as "MD5withRSA" for a signature algorithm on262* some systems. It also returns names like "OID.1.2.3.4", when263* no particular name for the algorithm is known.264*265* Note: for ecdsa-with-SHA2 plus hash algorithm (Ex: SHA-256), this method266* returns the "full" signature algorithm (Ex: SHA256withECDSA) directly.267*/268public String getName() {269String oidStr = algid.toString();270// first check the list of support oids271KnownOIDs o = KnownOIDs.findMatch(oidStr);272if (o == KnownOIDs.SpecifiedSHA2withECDSA) {273if (encodedParams != null) {274try {275AlgorithmId digestParams =276AlgorithmId.parse(new DerValue(encodedParams));277String digestAlg = digestParams.getName();278return digestAlg.replace("-", "") + "withECDSA";279} catch (IOException e) {280// ignore281}282}283}284if (o != null) {285return o.stdName();286} else {287String n = aliasOidsTable().get(oidStr);288if (n != null) {289return n;290} else {291return algid.toString();292}293}294}295296public AlgorithmParameters getParameters() {297return algParams;298}299300/**301* Returns the DER encoded parameter, which can then be302* used to initialize java.security.AlgorithmParameters.303*304* Note that this* method should always return a new array as it is called305* directly by the JDK implementation of X509Certificate.getSigAlgParams()306* and X509CRL.getSigAlgParams().307*308* Note: for ecdsa-with-SHA2 plus hash algorithm (Ex: SHA-256), this method309* returns null because {@link #getName()} has already returned the "full"310* signature algorithm (Ex: SHA256withECDSA).311*312* @return DER encoded parameters, or null not present.313*/314public byte[] getEncodedParams() throws IOException {315return (encodedParams == null ||316algid.toString().equals(KnownOIDs.SpecifiedSHA2withECDSA.value()))317? null318: encodedParams.clone();319}320321/**322* Returns true iff the argument indicates the same algorithm323* with the same parameters.324*/325public boolean equals(AlgorithmId other) {326return algid.equals((Object)other.algid) &&327Arrays.equals(encodedParams, other.encodedParams);328}329330/**331* Compares this AlgorithmID to another. If algorithm parameters are332* available, they are compared. Otherwise, just the object IDs333* for the algorithm are compared.334*335* @param other preferably an AlgorithmId, else an ObjectIdentifier336*/337@Override338public boolean equals(Object other) {339if (this == other) {340return true;341}342if (other instanceof AlgorithmId) {343return equals((AlgorithmId) other);344} else if (other instanceof ObjectIdentifier) {345return equals((ObjectIdentifier) other);346} else {347return false;348}349}350351/**352* Compares two algorithm IDs for equality. Returns true iff353* they are the same algorithm, ignoring algorithm parameters.354*/355public final boolean equals(ObjectIdentifier id) {356return algid.equals((Object)id);357}358359/**360* Returns a hashcode for this AlgorithmId.361*362* @return a hashcode for this AlgorithmId.363*/364@Override365public int hashCode() {366int hashCode = algid.hashCode();367hashCode = 31 * hashCode + Arrays.hashCode(encodedParams);368return hashCode;369}370371/**372* Provides a human-readable description of the algorithm parameters.373* This may be redefined by subclasses which parse those parameters.374*/375protected String paramsToString() {376if (encodedParams == null) {377return "";378} else if (algParams != null) {379return ", " + algParams.toString();380} else {381return ", params unparsed";382}383}384385/**386* Returns a string describing the algorithm and its parameters.387*/388@Override389public String toString() {390return getName() + paramsToString();391}392393/**394* Parse (unmarshal) an ID from a DER sequence input value. This form395* parsing might be used when expanding a value which has already been396* partially unmarshaled as a set or sequence member.397*398* @exception IOException on error.399* @param val the input value, which contains the algid and, if400* there are any parameters, those parameters.401* @return an ID for the algorithm. If the system is configured402* appropriately, this may be an instance of a class403* with some kind of special support for this algorithm.404* In that case, you may "narrow" the type of the ID.405*/406public static AlgorithmId parse(DerValue val) throws IOException {407if (val.tag != DerValue.tag_Sequence) {408throw new IOException("algid parse error, not a sequence");409}410411/*412* Get the algorithm ID and any parameters.413*/414ObjectIdentifier algid;415DerValue params;416DerInputStream in = val.toDerInputStream();417418algid = in.getOID();419if (in.available() == 0) {420params = null;421} else {422params = in.getDerValue();423if (params.tag == DerValue.tag_Null) {424if (params.length() != 0) {425throw new IOException("invalid NULL");426}427params = null;428}429if (in.available() != 0) {430throw new IOException("Invalid AlgorithmIdentifier: extra data");431}432}433434return new AlgorithmId(algid, params);435}436437/**438* Returns one of the algorithm IDs most commonly associated439* with this algorithm name.440*441* @param algname the name being used442* @deprecated use the short get form of this method.443* @exception NoSuchAlgorithmException on error.444*/445@Deprecated446public static AlgorithmId getAlgorithmId(String algname)447throws NoSuchAlgorithmException {448return get(algname);449}450451/**452* Returns one of the algorithm IDs most commonly associated453* with this algorithm name.454*455* @param algname the name being used456* @exception NoSuchAlgorithmException on error.457*/458public static AlgorithmId get(String algname)459throws NoSuchAlgorithmException {460ObjectIdentifier oid;461try {462oid = algOID(algname);463} catch (IOException ioe) {464throw new NoSuchAlgorithmException465("Invalid ObjectIdentifier " + algname);466}467468if (oid == null) {469throw new NoSuchAlgorithmException470("unrecognized algorithm name: " + algname);471}472return new AlgorithmId(oid);473}474475/**476* Returns one of the algorithm IDs most commonly associated477* with this algorithm parameters.478*479* @param algparams the associated algorithm parameters.480* @exception NoSuchAlgorithmException on error.481*/482public static AlgorithmId get(AlgorithmParameters algparams)483throws NoSuchAlgorithmException {484ObjectIdentifier oid;485String algname = algparams.getAlgorithm();486try {487oid = algOID(algname);488} catch (IOException ioe) {489throw new NoSuchAlgorithmException490("Invalid ObjectIdentifier " + algname);491}492if (oid == null) {493throw new NoSuchAlgorithmException494("unrecognized algorithm name: " + algname);495}496return new AlgorithmId(oid, algparams);497}498499/*500* Translates from some common algorithm names to the501* OID with which they're usually associated ... this mapping502* is the reverse of the one below, except in those cases503* where synonyms are supported or where a given algorithm504* is commonly associated with multiple OIDs.505*506* XXX This method needs to be enhanced so that we can also pass the507* scope of the algorithm name to it, e.g., the algorithm name "DSA"508* may have a different OID when used as a "Signature" algorithm than when509* used as a "KeyPairGenerator" algorithm.510*/511private static ObjectIdentifier algOID(String name) throws IOException {512if (name.startsWith("OID.")) {513name = name.substring("OID.".length());514}515516KnownOIDs k = KnownOIDs.findMatch(name);517if (k != null) {518return ObjectIdentifier.of(k);519}520521// unknown algorithm oids522if (name.indexOf(".") == -1) {523// see if there is a matching oid string alias mapping from524// 3rd party providers525name = name.toUpperCase(Locale.ENGLISH);526String oidStr = aliasOidsTable().get(name);527if (oidStr != null) {528return ObjectIdentifier.of(oidStr);529} return null;530} else {531return ObjectIdentifier.of(name);532}533}534535// oid string cache index'ed by algorithm name and oid strings536private static volatile Map<String,String> aliasOidsTable;537538// returns the aliasOidsTable, lazily initializing it on first access.539private static Map<String,String> aliasOidsTable() {540// Double checked locking; safe because aliasOidsTable is volatile541Map<String,String> tab = aliasOidsTable;542if (tab == null) {543synchronized (AlgorithmId.class) {544if ((tab = aliasOidsTable) == null) {545aliasOidsTable = tab = collectOIDAliases();546}547}548}549return tab;550}551552private static boolean isKnownProvider(Provider p) {553String pn = p.getName();554String mn = p.getClass().getModule().getName();555if (pn != null && mn != null) {556return ((mn.equals("java.base") &&557(pn.equals("SUN") || pn.equals("SunRsaSign") ||558pn.equals("SunJCE") || pn.equals("SunJSSE"))) ||559(mn.equals("jdk.crypto.ec") && pn.equals("SunEC")) ||560(mn.equals("jdk.crypto.mscapi") && pn.equals("SunMSCAPI")) ||561(mn.equals("jdk.crypto.cryptoki") &&562pn.startsWith("SunPKCS11")));563} else {564return false;565}566}567568private static ConcurrentHashMap<String, String> collectOIDAliases() {569ConcurrentHashMap<String, String> t = new ConcurrentHashMap<>();570for (Provider provider : Security.getProviders()) {571// skip providers which are already using SecurityProviderConstants572// and KnownOIDs573if (isKnownProvider(provider)) {574continue;575}576for (Object key : provider.keySet()) {577String alias = (String)key;578String upperCaseAlias = alias.toUpperCase(Locale.ENGLISH);579int index;580if (upperCaseAlias.startsWith("ALG.ALIAS") &&581(index = upperCaseAlias.indexOf("OID.", 0)) != -1) {582index += "OID.".length();583if (index == alias.length()) {584// invalid alias entry585break;586}587String ostr = alias.substring(index);588String stdAlgName = provider.getProperty(alias);589if (stdAlgName != null) {590stdAlgName = stdAlgName.toUpperCase(Locale.ENGLISH);591}592// add the name->oid and oid->name mappings if none exists593if (KnownOIDs.findMatch(stdAlgName) == null) {594// not override earlier entries if it exists595t.putIfAbsent(stdAlgName, ostr);596}597if (KnownOIDs.findMatch(ostr) == null) {598// not override earlier entries if it exists599t.putIfAbsent(ostr, stdAlgName);600}601}602}603}604return t;605}606607public static final ObjectIdentifier MD2_oid =608ObjectIdentifier.of(KnownOIDs.MD2);609610public static final ObjectIdentifier MD5_oid =611ObjectIdentifier.of(KnownOIDs.MD5);612613public static final ObjectIdentifier SHA_oid =614ObjectIdentifier.of(KnownOIDs.SHA_1);615616public static final ObjectIdentifier SHA224_oid =617ObjectIdentifier.of(KnownOIDs.SHA_224);618619public static final ObjectIdentifier SHA256_oid =620ObjectIdentifier.of(KnownOIDs.SHA_256);621622public static final ObjectIdentifier SHA384_oid =623ObjectIdentifier.of(KnownOIDs.SHA_384);624625public static final ObjectIdentifier SHA512_oid =626ObjectIdentifier.of(KnownOIDs.SHA_512);627628public static final ObjectIdentifier SHA512_224_oid =629ObjectIdentifier.of(KnownOIDs.SHA_512$224);630631public static final ObjectIdentifier SHA512_256_oid =632ObjectIdentifier.of(KnownOIDs.SHA_512$256);633634public static final ObjectIdentifier SHA3_224_oid =635ObjectIdentifier.of(KnownOIDs.SHA3_224);636637public static final ObjectIdentifier SHA3_256_oid =638ObjectIdentifier.of(KnownOIDs.SHA3_256);639640public static final ObjectIdentifier SHA3_384_oid =641ObjectIdentifier.of(KnownOIDs.SHA3_384);642643public static final ObjectIdentifier SHA3_512_oid =644ObjectIdentifier.of(KnownOIDs.SHA3_512);645646public static final ObjectIdentifier DSA_oid =647ObjectIdentifier.of(KnownOIDs.DSA);648649public static final ObjectIdentifier EC_oid =650ObjectIdentifier.of(KnownOIDs.EC);651652public static final ObjectIdentifier RSAEncryption_oid =653ObjectIdentifier.of(KnownOIDs.RSA);654655public static final ObjectIdentifier RSASSA_PSS_oid =656ObjectIdentifier.of(KnownOIDs.RSASSA_PSS);657658public static final ObjectIdentifier MGF1_oid =659ObjectIdentifier.of(KnownOIDs.MGF1);660661public static final ObjectIdentifier ed25519_oid =662ObjectIdentifier.of(KnownOIDs.Ed25519);663public static final ObjectIdentifier ed448_oid =664ObjectIdentifier.of(KnownOIDs.Ed448);665666public static final ObjectIdentifier x25519_oid =667ObjectIdentifier.of(KnownOIDs.X25519);668public static final ObjectIdentifier x448_oid =669ObjectIdentifier.of(KnownOIDs.X448);670671public static final ObjectIdentifier SHA224withECDSA_oid =672ObjectIdentifier.of(KnownOIDs.SHA224withECDSA);673public static final ObjectIdentifier SHA256withECDSA_oid =674ObjectIdentifier.of(KnownOIDs.SHA256withECDSA);675public static final ObjectIdentifier SHA384withECDSA_oid =676ObjectIdentifier.of(KnownOIDs.SHA384withECDSA);677public static final ObjectIdentifier SHA512withECDSA_oid =678ObjectIdentifier.of(KnownOIDs.SHA512withECDSA);679}680681682