|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  */ | 
|  |  | 
|  | package javax.crypto; | 
|  |  | 
|  | import java.io.*; | 
|  | import java.util.Enumeration; | 
|  | import java.util.Hashtable; | 
|  | import java.util.Vector; | 
|  | import static java.util.Locale.ENGLISH; | 
|  |  | 
|  | import java.security.GeneralSecurityException; | 
|  | import java.security.spec.AlgorithmParameterSpec; | 
|  | import java.lang.reflect.*; | 
|  |  | 
|  | /** | 
|  |  * JCE has two pairs of jurisdiction policy files: one represents U.S. export | 
|  |  * laws, and the other represents the local laws of the country where the | 
|  |  * JCE will be used. | 
|  |  * | 
|  |  * The jurisdiction policy file has the same syntax as JDK policy files except | 
|  |  * that JCE has new permission classes called javax.crypto.CryptoPermission | 
|  |  * and javax.crypto.CryptoAllPermission. | 
|  |  * | 
|  |  * The format of a permission entry in the jurisdiction policy file is: | 
|  |  * | 
|  |  *   permission <crypto permission class name>[, <algorithm name> | 
|  |  *              [[, <exemption mechanism name>][, <maxKeySize> | 
|  |  *              [, <AlgrithomParameterSpec class name>, <parameters | 
|  |  *              for constructing an AlgrithomParameterSpec object>]]]]; | 
|  |  * | 
|  |  * @author Sharon Liu | 
|  |  * | 
|  |  * @see java.security.Permissions | 
|  |  * @see java.security.spec.AlgorithmParameterSpec | 
|  |  * @see javax.crypto.CryptoPermission | 
|  |  * @see javax.crypto.CryptoAllPermission | 
|  |  * @see javax.crypto.CryptoPermissions | 
|  |  * @since 1.4 | 
|  |  */ | 
|  |  | 
|  | final class CryptoPolicyParser { | 
|  |  | 
|  |     private Vector<GrantEntry> grantEntries; | 
|  |  | 
|  |      | 
|  |     private StreamTokenizer st; | 
|  |     private int lookahead; | 
|  |  | 
|  |      | 
|  |  | 
|  |      */ | 
|  |     CryptoPolicyParser() { | 
|  |         grantEntries = new Vector<GrantEntry>(); | 
|  |     } | 
|  |  | 
|  |     /** | 
|  |      * Reads a policy configuration using a Reader object. <p> | 
|  |      * | 
|  |      * @param policy the policy Reader object. | 
|  |      * | 
|  |      * @exception ParsingException if the policy configuration | 
|  |      * contains a syntax error. | 
|  |      * | 
|  |      * @exception IOException if an error occurs while reading | 
|  |      * the policy configuration. | 
|  |      */ | 
|  |  | 
|  |     void read(Reader policy) | 
|  |         throws ParsingException, IOException | 
|  |     { | 
|  |         if (!(policy instanceof BufferedReader)) { | 
|  |             policy = new BufferedReader(policy); | 
|  |         } | 
|  |  | 
|  |          | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |          */ | 
|  |         st = new StreamTokenizer(policy); | 
|  |  | 
|  |         st.resetSyntax(); | 
|  |         st.wordChars('a', 'z'); | 
|  |         st.wordChars('A', 'Z'); | 
|  |         st.wordChars('.', '.'); | 
|  |         st.wordChars('0', '9'); | 
|  |         st.wordChars('_', '_'); | 
|  |         st.wordChars('$', '$'); | 
|  |         st.wordChars(128 + 32, 255); | 
|  |         st.whitespaceChars(0, ' '); | 
|  |         st.commentChar('/'); | 
|  |         st.quoteChar('\''); | 
|  |         st.quoteChar('"'); | 
|  |         st.lowerCaseMode(false); | 
|  |         st.ordinaryChar('/'); | 
|  |         st.slashSlashComments(true); | 
|  |         st.slashStarComments(true); | 
|  |         st.parseNumbers(); | 
|  |  | 
|  |          | 
|  |  | 
|  |  | 
|  |          */ | 
|  |         Hashtable<String, Vector<String>> processedPermissions = null; | 
|  |  | 
|  |          | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |          */ | 
|  |         lookahead = st.nextToken(); | 
|  |         while (lookahead != StreamTokenizer.TT_EOF) { | 
|  |             if (peek("grant")) { | 
|  |                 GrantEntry ge = parseGrantEntry(processedPermissions); | 
|  |                 if (ge != null) | 
|  |                     grantEntries.addElement(ge); | 
|  |             } else { | 
|  |                 throw new ParsingException(st.lineno(), "expected grant " + | 
|  |                                            "statement"); | 
|  |             } | 
|  |             match(";"); | 
|  |         } | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |      */ | 
|  |     private GrantEntry parseGrantEntry( | 
|  |             Hashtable<String, Vector<String>> processedPermissions) | 
|  |         throws ParsingException, IOException | 
|  |     { | 
|  |         GrantEntry e = new GrantEntry(); | 
|  |  | 
|  |         match("grant"); | 
|  |         match("{"); | 
|  |  | 
|  |         while(!peek("}")) { | 
|  |             if (peek("Permission")) { | 
|  |                 CryptoPermissionEntry pe = | 
|  |                     parsePermissionEntry(processedPermissions); | 
|  |                 e.add(pe); | 
|  |                 match(";"); | 
|  |             } else { | 
|  |                 throw new | 
|  |                     ParsingException(st.lineno(), "expected permission entry"); | 
|  |             } | 
|  |         } | 
|  |         match("}"); | 
|  |  | 
|  |         return e; | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |      */ | 
|  |     private CryptoPermissionEntry parsePermissionEntry( | 
|  |             Hashtable<String, Vector<String>> processedPermissions) | 
|  |         throws ParsingException, IOException | 
|  |     { | 
|  |         CryptoPermissionEntry e = new CryptoPermissionEntry(); | 
|  |  | 
|  |         match("Permission"); | 
|  |         e.cryptoPermission = match("permission type"); | 
|  |  | 
|  |         if (e.cryptoPermission.equals("javax.crypto.CryptoAllPermission")) { | 
|  |              | 
|  |             e.alg = CryptoAllPermission.ALG_NAME; | 
|  |             e.maxKeySize = Integer.MAX_VALUE; | 
|  |             return e; | 
|  |         } | 
|  |  | 
|  |          | 
|  |         if (peek("\"")) { | 
|  |              | 
|  |             e.alg = match("quoted string").toUpperCase(ENGLISH); | 
|  |         } else { | 
|  |              | 
|  |             if (peek("*")) { | 
|  |                 match("*"); | 
|  |                 e.alg = CryptoPermission.ALG_NAME_WILDCARD; | 
|  |             } else { | 
|  |                 throw new ParsingException(st.lineno(), | 
|  |                                            "Missing the algorithm name"); | 
|  |             } | 
|  |         } | 
|  |  | 
|  |         peekAndMatch(","); | 
|  |  | 
|  |          | 
|  |         if (peek("\"")) { | 
|  |              | 
|  |             e.exemptionMechanism = match("quoted string").toUpperCase(ENGLISH); | 
|  |         } | 
|  |  | 
|  |         peekAndMatch(","); | 
|  |  | 
|  |         // Check whether this entry is consistent with other permission entries | 
|  |          | 
|  |         if (!isConsistent(e.alg, e.exemptionMechanism, processedPermissions)) { | 
|  |             throw new ParsingException(st.lineno(), "Inconsistent policy"); | 
|  |         } | 
|  |  | 
|  |          | 
|  |         if (peek("number")) { | 
|  |             e.maxKeySize = match(); | 
|  |         } else { | 
|  |             if (peek("*")) { | 
|  |                 match("*"); | 
|  |                 e.maxKeySize = Integer.MAX_VALUE; | 
|  |             } else { | 
|  |                 if (!peek(";")) { | 
|  |                     throw new ParsingException(st.lineno(), | 
|  |                                                "Missing the maximum " + | 
|  |                                                "allowable key size"); | 
|  |                 } else { | 
|  |                      | 
|  |                     e.maxKeySize = Integer.MAX_VALUE; | 
|  |                 } | 
|  |             } | 
|  |         } | 
|  |  | 
|  |         peekAndMatch(","); | 
|  |  | 
|  |          | 
|  |         if (peek("\"")) { | 
|  |              | 
|  |             String algParamSpecClassName = match("quoted string"); | 
|  |  | 
|  |             Vector<Integer> paramsV = new Vector<>(1); | 
|  |             while (peek(",")) { | 
|  |                 match(","); | 
|  |                 if (peek("number")) { | 
|  |                     paramsV.addElement(new Integer(match())); | 
|  |                 } else { | 
|  |                     if (peek("*")) { | 
|  |                         match("*"); | 
|  |                         paramsV.addElement(new Integer(Integer.MAX_VALUE)); | 
|  |                     } else { | 
|  |                         throw new ParsingException(st.lineno(), | 
|  |                                                    "Expecting an integer"); | 
|  |                     } | 
|  |                 } | 
|  |             } | 
|  |  | 
|  |             Integer[] params = new Integer[paramsV.size()]; | 
|  |             paramsV.copyInto(params); | 
|  |  | 
|  |             e.checkParam = true; | 
|  |             e.algParamSpec = getInstance(algParamSpecClassName, params); | 
|  |         } | 
|  |  | 
|  |         return e; | 
|  |     } | 
|  |  | 
|  |     private static final AlgorithmParameterSpec getInstance(String type, | 
|  |                                                             Integer[] params) | 
|  |         throws ParsingException | 
|  |     { | 
|  |         AlgorithmParameterSpec ret = null; | 
|  |  | 
|  |         try { | 
|  |             Class<?> apsClass = Class.forName(type); | 
|  |             Class<?>[] paramClasses = new Class<?>[params.length]; | 
|  |  | 
|  |             for (int i = 0; i < params.length; i++) { | 
|  |                 paramClasses[i] = int.class; | 
|  |             } | 
|  |  | 
|  |             Constructor<?> c = apsClass.getConstructor(paramClasses); | 
|  |             ret = (AlgorithmParameterSpec) c.newInstance((Object[]) params); | 
|  |         } catch (Exception e) { | 
|  |             throw new ParsingException("Cannot call the constructor of " + | 
|  |                                        type + e); | 
|  |         } | 
|  |         return ret; | 
|  |     } | 
|  |  | 
|  |  | 
|  |     private boolean peekAndMatch(String expect) | 
|  |         throws ParsingException, IOException | 
|  |     { | 
|  |         if (peek(expect)) { | 
|  |             match(expect); | 
|  |             return true; | 
|  |         } | 
|  |         return false; | 
|  |     } | 
|  |  | 
|  |     private boolean peek(String expect) { | 
|  |         boolean found = false; | 
|  |  | 
|  |         switch (lookahead) { | 
|  |  | 
|  |         case StreamTokenizer.TT_WORD: | 
|  |             if (expect.equalsIgnoreCase(st.sval)) | 
|  |                 found = true; | 
|  |             break; | 
|  |         case StreamTokenizer.TT_NUMBER: | 
|  |             if (expect.equalsIgnoreCase("number")) { | 
|  |                 found = true; | 
|  |             } | 
|  |             break; | 
|  |         case ',': | 
|  |             if (expect.equals(",")) | 
|  |                 found = true; | 
|  |             break; | 
|  |         case '{': | 
|  |             if (expect.equals("{")) | 
|  |                 found = true; | 
|  |             break; | 
|  |         case '}': | 
|  |             if (expect.equals("}")) | 
|  |                 found = true; | 
|  |             break; | 
|  |         case '"': | 
|  |             if (expect.equals("\"")) | 
|  |                 found = true; | 
|  |             break; | 
|  |         case '*': | 
|  |             if (expect.equals("*")) | 
|  |                 found = true; | 
|  |             break; | 
|  |         case ';': | 
|  |             if (expect.equals(";")) | 
|  |                 found = true; | 
|  |             break; | 
|  |         default: | 
|  |             break; | 
|  |         } | 
|  |         return found; | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |      */ | 
|  |     private int match() | 
|  |         throws ParsingException, IOException | 
|  |     { | 
|  |         int value = -1; | 
|  |         int lineno = st.lineno(); | 
|  |         String sValue = null; | 
|  |  | 
|  |         switch (lookahead) { | 
|  |         case StreamTokenizer.TT_NUMBER: | 
|  |             value = (int)st.nval; | 
|  |             if (value < 0) { | 
|  |                 sValue = String.valueOf(st.nval); | 
|  |             } | 
|  |             lookahead = st.nextToken(); | 
|  |             break; | 
|  |         default: | 
|  |             sValue = st.sval; | 
|  |             break; | 
|  |         } | 
|  |         if (value <= 0) { | 
|  |             throw new ParsingException(lineno, "a non-negative number", | 
|  |                                        sValue); | 
|  |         } | 
|  |         return value; | 
|  |     } | 
|  |  | 
|  |     private String match(String expect) | 
|  |         throws ParsingException, IOException | 
|  |     { | 
|  |         String value = null; | 
|  |  | 
|  |         switch (lookahead) { | 
|  |         case StreamTokenizer.TT_NUMBER: | 
|  |             throw new ParsingException(st.lineno(), expect, | 
|  |                                        "number "+String.valueOf(st.nval)); | 
|  |         case StreamTokenizer.TT_EOF: | 
|  |            throw new ParsingException("expected "+expect+", read end of file"); | 
|  |         case StreamTokenizer.TT_WORD: | 
|  |             if (expect.equalsIgnoreCase(st.sval)) { | 
|  |                 lookahead = st.nextToken(); | 
|  |             } | 
|  |             else if (expect.equalsIgnoreCase("permission type")) { | 
|  |                 value = st.sval; | 
|  |                 lookahead = st.nextToken(); | 
|  |             } | 
|  |             else | 
|  |                 throw new ParsingException(st.lineno(), expect, st.sval); | 
|  |             break; | 
|  |         case '"': | 
|  |             if (expect.equalsIgnoreCase("quoted string")) { | 
|  |                 value = st.sval; | 
|  |                 lookahead = st.nextToken(); | 
|  |             } else if (expect.equalsIgnoreCase("permission type")) { | 
|  |                 value = st.sval; | 
|  |                 lookahead = st.nextToken(); | 
|  |             } | 
|  |             else | 
|  |                 throw new ParsingException(st.lineno(), expect, st.sval); | 
|  |             break; | 
|  |         case ',': | 
|  |             if (expect.equals(",")) | 
|  |                 lookahead = st.nextToken(); | 
|  |             else | 
|  |                 throw new ParsingException(st.lineno(), expect, ","); | 
|  |             break; | 
|  |         case '{': | 
|  |             if (expect.equals("{")) | 
|  |                 lookahead = st.nextToken(); | 
|  |             else | 
|  |                 throw new ParsingException(st.lineno(), expect, "{"); | 
|  |             break; | 
|  |         case '}': | 
|  |             if (expect.equals("}")) | 
|  |                 lookahead = st.nextToken(); | 
|  |             else | 
|  |                 throw new ParsingException(st.lineno(), expect, "}"); | 
|  |             break; | 
|  |         case ';': | 
|  |             if (expect.equals(";")) | 
|  |                 lookahead = st.nextToken(); | 
|  |             else | 
|  |                 throw new ParsingException(st.lineno(), expect, ";"); | 
|  |             break; | 
|  |         case '*': | 
|  |             if (expect.equals("*")) | 
|  |                 lookahead = st.nextToken(); | 
|  |             else | 
|  |                 throw new ParsingException(st.lineno(), expect, "*"); | 
|  |             break; | 
|  |         default: | 
|  |             throw new ParsingException(st.lineno(), expect, | 
|  |                                new String(new char[] {(char)lookahead})); | 
|  |         } | 
|  |         return value; | 
|  |     } | 
|  |  | 
|  |     CryptoPermission[] getPermissions() { | 
|  |         Vector<CryptoPermission> result = new Vector<>(); | 
|  |  | 
|  |         Enumeration<GrantEntry> grantEnum = grantEntries.elements(); | 
|  |         while (grantEnum.hasMoreElements()) { | 
|  |             GrantEntry ge = grantEnum.nextElement(); | 
|  |             Enumeration<CryptoPermissionEntry> permEnum = | 
|  |                     ge.permissionElements(); | 
|  |             while (permEnum.hasMoreElements()) { | 
|  |                 CryptoPermissionEntry pe = permEnum.nextElement(); | 
|  |                 if (pe.cryptoPermission.equals( | 
|  |                                         "javax.crypto.CryptoAllPermission")) { | 
|  |                     result.addElement(CryptoAllPermission.INSTANCE); | 
|  |                 } else { | 
|  |                     if (pe.checkParam) { | 
|  |                         result.addElement(new CryptoPermission( | 
|  |                                                 pe.alg, | 
|  |                                                 pe.maxKeySize, | 
|  |                                                 pe.algParamSpec, | 
|  |                                                 pe.exemptionMechanism)); | 
|  |                     } else { | 
|  |                         result.addElement(new CryptoPermission( | 
|  |                                                 pe.alg, | 
|  |                                                 pe.maxKeySize, | 
|  |                                                 pe.exemptionMechanism)); | 
|  |                     } | 
|  |                 } | 
|  |             } | 
|  |         } | 
|  |  | 
|  |         CryptoPermission[] ret = new CryptoPermission[result.size()]; | 
|  |         result.copyInto(ret); | 
|  |  | 
|  |         return ret; | 
|  |     } | 
|  |  | 
|  |     private boolean isConsistent(String alg, String exemptionMechanism, | 
|  |             Hashtable<String, Vector<String>> processedPermissions) { | 
|  |         String thisExemptionMechanism = | 
|  |             exemptionMechanism == null ? "none" : exemptionMechanism; | 
|  |  | 
|  |         if (processedPermissions == null) { | 
|  |             processedPermissions = new Hashtable<String, Vector<String>>(); | 
|  |             Vector<String> exemptionMechanisms = new Vector<>(1); | 
|  |             exemptionMechanisms.addElement(thisExemptionMechanism); | 
|  |             processedPermissions.put(alg, exemptionMechanisms); | 
|  |             return true; | 
|  |         } | 
|  |  | 
|  |         if (processedPermissions.containsKey(CryptoAllPermission.ALG_NAME)) { | 
|  |             return false; | 
|  |         } | 
|  |  | 
|  |         Vector<String> exemptionMechanisms; | 
|  |  | 
|  |         if (processedPermissions.containsKey(alg)) { | 
|  |             exemptionMechanisms = processedPermissions.get(alg); | 
|  |             if (exemptionMechanisms.contains(thisExemptionMechanism)) { | 
|  |                 return false; | 
|  |             } | 
|  |         } else { | 
|  |             exemptionMechanisms = new Vector<String>(1); | 
|  |         } | 
|  |  | 
|  |         exemptionMechanisms.addElement(thisExemptionMechanism); | 
|  |         processedPermissions.put(alg, exemptionMechanisms); | 
|  |         return true; | 
|  |     } | 
|  |  | 
|  |     /** | 
|  |      * Each grant entry in the policy configuration file is  represented by a | 
|  |      * GrantEntry object.  <p> | 
|  |      * | 
|  |      * <p> | 
|  |      * For example, the entry | 
|  |      * <pre> | 
|  |      *      grant { | 
|  |      *       permission javax.crypto.CryptoPermission "DES", 56; | 
|  |      *      }; | 
|  |      * | 
|  |      * </pre> | 
|  |      * is represented internally | 
|  |      * <pre> | 
|  |      * | 
|  |      * pe = new CryptoPermissionEntry("javax.crypto.CryptoPermission", | 
|  |      *                           "DES", 56); | 
|  |      * | 
|  |      * ge = new GrantEntry(); | 
|  |      * | 
|  |      * ge.add(pe); | 
|  |      * | 
|  |      * </pre> | 
|  |      * | 
|  |      * @see java.security.Permission | 
|  |      * @see javax.crypto.CryptoPermission | 
|  |      * @see javax.crypto.CryptoPermissions | 
|  |      */ | 
|  |  | 
|  |     private static class GrantEntry { | 
|  |  | 
|  |         private Vector<CryptoPermissionEntry> permissionEntries; | 
|  |  | 
|  |         GrantEntry() { | 
|  |             permissionEntries = new Vector<CryptoPermissionEntry>(); | 
|  |         } | 
|  |  | 
|  |         void add(CryptoPermissionEntry pe) | 
|  |         { | 
|  |             permissionEntries.addElement(pe); | 
|  |         } | 
|  |  | 
|  |         boolean remove(CryptoPermissionEntry pe) | 
|  |         { | 
|  |             return permissionEntries.removeElement(pe); | 
|  |         } | 
|  |  | 
|  |         boolean contains(CryptoPermissionEntry pe) | 
|  |         { | 
|  |             return permissionEntries.contains(pe); | 
|  |         } | 
|  |  | 
|  |          | 
|  |  | 
|  |          */ | 
|  |         Enumeration<CryptoPermissionEntry> permissionElements(){ | 
|  |             return permissionEntries.elements(); | 
|  |         } | 
|  |  | 
|  |     } | 
|  |  | 
|  |     /** | 
|  |      * Each crypto permission entry in the policy configuration file is | 
|  |      * represented by a CryptoPermissionEntry object.  <p> | 
|  |      * | 
|  |      * <p> | 
|  |      * For example, the entry | 
|  |      * <pre> | 
|  |      *     permission javax.crypto.CryptoPermission "DES", 56; | 
|  |      * </pre> | 
|  |      * is represented internally | 
|  |      * <pre> | 
|  |      * | 
|  |      * pe = new CryptoPermissionEntry("javax.crypto.cryptoPermission", | 
|  |      *                           "DES", 56); | 
|  |      * </pre> | 
|  |      * | 
|  |      * @see java.security.Permissions | 
|  |      * @see javax.crypto.CryptoPermission | 
|  |      * @see javax.crypto.CryptoAllPermission | 
|  |      */ | 
|  |  | 
|  |     private static class CryptoPermissionEntry { | 
|  |  | 
|  |         String cryptoPermission; | 
|  |         String alg; | 
|  |         String exemptionMechanism; | 
|  |         int maxKeySize; | 
|  |         boolean checkParam; | 
|  |         AlgorithmParameterSpec algParamSpec; | 
|  |  | 
|  |         CryptoPermissionEntry() { | 
|  |              | 
|  |             maxKeySize = 0; | 
|  |             alg = null; | 
|  |             exemptionMechanism = null; | 
|  |             checkParam = false; | 
|  |             algParamSpec = null; | 
|  |         } | 
|  |  | 
|  |          | 
|  |  | 
|  |  | 
|  |          */ | 
|  |         public int hashCode() { | 
|  |             int retval = cryptoPermission.hashCode(); | 
|  |             if (alg != null) retval ^= alg.hashCode(); | 
|  |             if (exemptionMechanism != null) { | 
|  |                 retval ^= exemptionMechanism.hashCode(); | 
|  |             } | 
|  |             retval ^= maxKeySize; | 
|  |             if (checkParam) retval ^= 100; | 
|  |             if (algParamSpec != null) { | 
|  |                 retval ^= algParamSpec.hashCode(); | 
|  |             } | 
|  |             return retval; | 
|  |         } | 
|  |  | 
|  |         public boolean equals(Object obj) { | 
|  |             if (obj == this) | 
|  |                 return true; | 
|  |  | 
|  |             if (!(obj instanceof CryptoPermissionEntry)) | 
|  |                 return false; | 
|  |  | 
|  |             CryptoPermissionEntry that = (CryptoPermissionEntry) obj; | 
|  |  | 
|  |             if (this.cryptoPermission == null) { | 
|  |                 if (that.cryptoPermission != null) return false; | 
|  |             } else { | 
|  |                 if (!this.cryptoPermission.equals( | 
|  |                                                  that.cryptoPermission)) | 
|  |                     return false; | 
|  |             } | 
|  |  | 
|  |             if (this.alg == null) { | 
|  |                 if (that.alg != null) return false; | 
|  |             } else { | 
|  |                 if (!this.alg.equalsIgnoreCase(that.alg)) | 
|  |                     return false; | 
|  |             } | 
|  |  | 
|  |             if (!(this.maxKeySize == that.maxKeySize)) return false; | 
|  |  | 
|  |             if (this.checkParam != that.checkParam) return false; | 
|  |  | 
|  |             if (this.algParamSpec == null) { | 
|  |                 if (that.algParamSpec != null) return false; | 
|  |             } else { | 
|  |                 if (!this.algParamSpec.equals(that.algParamSpec)) | 
|  |                     return false; | 
|  |             } | 
|  |  | 
|  |              | 
|  |             return true; | 
|  |         } | 
|  |     } | 
|  |  | 
|  |     static final class ParsingException extends GeneralSecurityException { | 
|  |  | 
|  |         private static final long serialVersionUID = 7147241245566588374L; | 
|  |  | 
|  |          | 
|  |  | 
|  |  | 
|  |  | 
|  |          */ | 
|  |         ParsingException(String msg) { | 
|  |             super(msg); | 
|  |         } | 
|  |  | 
|  |         ParsingException(int line, String msg) { | 
|  |             super("line " + line + ": " + msg); | 
|  |         } | 
|  |  | 
|  |         ParsingException(int line, String expect, String actual) { | 
|  |             super("line "+line+": expected '"+expect+"', found '"+actual+"'"); | 
|  |         } | 
|  |     } | 
|  | } |