src/java.base/share/classes/sun/security/rsa/RSAPSSSignature.java
branchJDK-8145252-TLS13-branch
changeset 56542 56aaa6cb3693
child 56592 b1902b22005e
equal deleted inserted replaced
56541:92cbbfc996f3 56542:56aaa6cb3693
       
     1 /*
       
     2  * Copyright (c) 2018, 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.rsa;
       
    27 
       
    28 import java.io.IOException;
       
    29 import java.nio.ByteBuffer;
       
    30 
       
    31 import java.security.*;
       
    32 import java.security.spec.AlgorithmParameterSpec;
       
    33 import java.security.spec.PSSParameterSpec;
       
    34 import java.security.spec.MGF1ParameterSpec;
       
    35 import java.security.interfaces.*;
       
    36 
       
    37 import java.util.Arrays;
       
    38 import java.util.Hashtable;
       
    39 
       
    40 import sun.security.util.*;
       
    41 import sun.security.jca.JCAUtil;
       
    42 
       
    43 
       
    44 /**
       
    45  * PKCS#1 v2.2 RSASSA-PSS signatures with various message digest algorithms.
       
    46  * RSASSA-PSS implementation takes the message digest algorithm, MGF algorithm,
       
    47  * and salt length values through the required signature PSS parameters.
       
    48  * We support SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, and
       
    49  * SHA-512/256 message digest algorithms and MGF1 mask generation function.
       
    50  *
       
    51  * @since   11
       
    52  */
       
    53 public class RSAPSSSignature extends SignatureSpi {
       
    54 
       
    55     private static final boolean DEBUG = false;
       
    56 
       
    57     // utility method for comparing digest algorithms
       
    58     // NOTE that first argument is assumed to be standard digest name
       
    59     private boolean isDigestEqual(String stdAlg, String givenAlg) {
       
    60         if (stdAlg == null || givenAlg == null) return false;
       
    61 
       
    62         if (givenAlg.indexOf("-") != -1) {
       
    63             return stdAlg.equalsIgnoreCase(givenAlg);
       
    64         } else {
       
    65             if (stdAlg.equals("SHA-1")) {
       
    66                 return (givenAlg.equalsIgnoreCase("SHA")
       
    67                         || givenAlg.equalsIgnoreCase("SHA1"));
       
    68             } else {
       
    69                 StringBuilder sb = new StringBuilder(givenAlg);
       
    70                 // case-insensitive check
       
    71                 if (givenAlg.regionMatches(true, 0, "SHA", 0, 3)) {
       
    72                     givenAlg = sb.insert(3, "-").toString();
       
    73                     return stdAlg.equalsIgnoreCase(givenAlg);
       
    74                 } else {
       
    75                     throw new ProviderException("Unsupported digest algorithm "
       
    76                             + givenAlg);
       
    77                 }
       
    78             }
       
    79         }
       
    80     }
       
    81 
       
    82     private static final byte[] EIGHT_BYTES_OF_ZEROS = new byte[8];
       
    83 
       
    84     private static final Hashtable<String, Integer> DIGEST_LENGTHS =
       
    85         new Hashtable<String, Integer>();
       
    86     static {
       
    87         DIGEST_LENGTHS.put("SHA-1", 20);
       
    88         DIGEST_LENGTHS.put("SHA", 20);
       
    89         DIGEST_LENGTHS.put("SHA1", 20);
       
    90         DIGEST_LENGTHS.put("SHA-224", 28);
       
    91         DIGEST_LENGTHS.put("SHA224", 28);
       
    92         DIGEST_LENGTHS.put("SHA-256", 32);
       
    93         DIGEST_LENGTHS.put("SHA256", 32);
       
    94         DIGEST_LENGTHS.put("SHA-384", 48);
       
    95         DIGEST_LENGTHS.put("SHA384", 48);
       
    96         DIGEST_LENGTHS.put("SHA-512", 64);
       
    97         DIGEST_LENGTHS.put("SHA512", 64);
       
    98         DIGEST_LENGTHS.put("SHA-512/224", 28);
       
    99         DIGEST_LENGTHS.put("SHA512/224", 28);
       
   100         DIGEST_LENGTHS.put("SHA-512/256", 32);
       
   101         DIGEST_LENGTHS.put("SHA512/256", 32);
       
   102     }
       
   103 
       
   104     // message digest implementation we use for hashing the data
       
   105     private MessageDigest md;
       
   106     // flag indicating whether the digest is reset
       
   107     private boolean digestReset = true;
       
   108 
       
   109     // private key, if initialized for signing
       
   110     private RSAPrivateKey privKey = null;
       
   111     // public key, if initialized for verifying
       
   112     private RSAPublicKey pubKey = null;
       
   113     // PSS parameters from signatures and keys respectively
       
   114     private PSSParameterSpec sigParams = null; // required for PSS signatures
       
   115 
       
   116     // PRNG used to generate salt bytes if none given
       
   117     private SecureRandom random;
       
   118 
       
   119     /**
       
   120      * Construct a new RSAPSSSignatur with arbitrary digest algorithm
       
   121      */
       
   122     public RSAPSSSignature() {
       
   123         this.md = null;
       
   124     }
       
   125 
       
   126     // initialize for verification. See JCA doc
       
   127     @Override
       
   128     protected void engineInitVerify(PublicKey publicKey)
       
   129             throws InvalidKeyException {
       
   130         if (!(publicKey instanceof RSAPublicKey)) {
       
   131             throw new InvalidKeyException("key must be RSAPublicKey");
       
   132         }
       
   133         this.pubKey = (RSAPublicKey) isValid((RSAKey)publicKey);
       
   134         this.privKey = null;
       
   135 
       
   136     }
       
   137 
       
   138     // initialize for signing. See JCA doc
       
   139     @Override
       
   140     protected void engineInitSign(PrivateKey privateKey)
       
   141             throws InvalidKeyException {
       
   142         engineInitSign(privateKey, null);
       
   143     }
       
   144 
       
   145     // initialize for signing. See JCA doc
       
   146     @Override
       
   147     protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
       
   148             throws InvalidKeyException {
       
   149         if (!(privateKey instanceof RSAPrivateKey)) {
       
   150             throw new InvalidKeyException("key must be RSAPrivateKey");
       
   151         }
       
   152         this.privKey = (RSAPrivateKey) isValid((RSAKey)privateKey);
       
   153         this.pubKey = null;
       
   154         this.random =
       
   155             (random == null? JCAUtil.getSecureRandom() : random);
       
   156     }
       
   157 
       
   158     /**
       
   159      * Utility method for checking the key PSS parameters against signature
       
   160      * PSS parameters.
       
   161      * Returns false if any of the digest/MGF algorithms and trailerField
       
   162      * values does not match or if the salt length in key parameters is
       
   163      * larger than the value in signature parameters.
       
   164      */
       
   165     private static boolean isCompatible(AlgorithmParameterSpec keyParams,
       
   166             PSSParameterSpec sigParams) {
       
   167         if (keyParams == null) {
       
   168             // key with null PSS parameters means no restriction
       
   169             return true;
       
   170         }
       
   171         if (!(keyParams instanceof PSSParameterSpec)) {
       
   172             return false;
       
   173         }
       
   174         // nothing to compare yet, defer the check to when sigParams is set
       
   175         if (sigParams == null) {
       
   176             return true;
       
   177         }
       
   178         PSSParameterSpec pssKeyParams = (PSSParameterSpec) keyParams;
       
   179         // first check the salt length requirement
       
   180         if (pssKeyParams.getSaltLength() > sigParams.getSaltLength()) {
       
   181             return false;
       
   182         }
       
   183 
       
   184         // compare equality of the rest of fields based on DER encoding
       
   185         PSSParameterSpec keyParams2 =
       
   186             new PSSParameterSpec(pssKeyParams.getDigestAlgorithm(),
       
   187                     pssKeyParams.getMGFAlgorithm(),
       
   188                     pssKeyParams.getMGFParameters(),
       
   189                     sigParams.getSaltLength(),
       
   190                     pssKeyParams.getTrailerField());
       
   191         PSSParameters ap = new PSSParameters();
       
   192         try {
       
   193             ap.engineInit(keyParams2);
       
   194             byte[] encoded = ap.engineGetEncoded();
       
   195             ap.engineInit(sigParams);
       
   196             byte[] encoded2 = ap.engineGetEncoded();
       
   197             return Arrays.equals(encoded, encoded2);
       
   198         } catch (Exception e) {
       
   199             if (DEBUG) {
       
   200                 e.printStackTrace();
       
   201             }
       
   202             return false;
       
   203         }
       
   204     }
       
   205 
       
   206     /**
       
   207      * Validate the specified RSAKey and its associated parameters against
       
   208      * internal signature parameters.
       
   209      */
       
   210     private RSAKey isValid(RSAKey rsaKey) throws InvalidKeyException {
       
   211         try {
       
   212             AlgorithmParameterSpec keyParams = rsaKey.getParams();
       
   213             // validate key parameters
       
   214             if (!isCompatible(rsaKey.getParams(), this.sigParams)) {
       
   215                 throw new InvalidKeyException
       
   216                     ("Key contains incompatible PSS parameter values");
       
   217             }
       
   218             // validate key length
       
   219             if (this.sigParams != null) {
       
   220                 Integer hLen =
       
   221                     DIGEST_LENGTHS.get(this.sigParams.getDigestAlgorithm());
       
   222                 if (hLen == null) {
       
   223                     throw new ProviderException("Unsupported digest algo: " +
       
   224                         this.sigParams.getDigestAlgorithm());
       
   225                 }
       
   226                 checkKeyLength(rsaKey, hLen, this.sigParams.getSaltLength());
       
   227             }
       
   228             return rsaKey;
       
   229         } catch (SignatureException e) {
       
   230             throw new InvalidKeyException(e);
       
   231         }
       
   232     }
       
   233 
       
   234     /**
       
   235      * Validate the specified Signature PSS parameters.
       
   236      */
       
   237     private PSSParameterSpec validateSigParams(AlgorithmParameterSpec p)
       
   238             throws InvalidAlgorithmParameterException {
       
   239         if (p == null) {
       
   240             throw new InvalidAlgorithmParameterException
       
   241                 ("Parameters cannot be null");
       
   242         }
       
   243         if (!(p instanceof PSSParameterSpec)) {
       
   244             throw new InvalidAlgorithmParameterException
       
   245                 ("parameters must be type PSSParameterSpec");
       
   246         }
       
   247         // no need to validate again if same as current signature parameters
       
   248         PSSParameterSpec params = (PSSParameterSpec) p;
       
   249         if (params == this.sigParams) return params;
       
   250 
       
   251         RSAKey key = (this.privKey == null? this.pubKey : this.privKey);
       
   252         // check against keyParams if set
       
   253         if (key != null) {
       
   254             if (!isCompatible(key.getParams(), params)) {
       
   255                 throw new InvalidAlgorithmParameterException
       
   256                     ("Signature parameters does not match key parameters");
       
   257             }
       
   258         }
       
   259         // now sanity check the parameter values
       
   260         if (!(params.getMGFAlgorithm().equalsIgnoreCase("MGF1"))) {
       
   261             throw new InvalidAlgorithmParameterException("Only supports MGF1");
       
   262 
       
   263         }
       
   264         if (params.getTrailerField() != 1) {
       
   265             throw new InvalidAlgorithmParameterException
       
   266                 ("Only supports TrailerFieldBC(1)");
       
   267 
       
   268         }
       
   269         String digestAlgo = params.getDigestAlgorithm();
       
   270         // check key length again
       
   271         if (key != null) {
       
   272             try {
       
   273                 int hLen = DIGEST_LENGTHS.get(digestAlgo);
       
   274                 checkKeyLength(key, hLen, params.getSaltLength());
       
   275             } catch (SignatureException e) {
       
   276                 throw new InvalidAlgorithmParameterException(e);
       
   277             }
       
   278         }
       
   279         return params;
       
   280     }
       
   281 
       
   282     /**
       
   283      * Ensure the object is initialized with key and parameters and
       
   284      * reset digest
       
   285      */
       
   286     private void ensureInit() throws SignatureException {
       
   287         RSAKey key = (this.privKey == null? this.pubKey : this.privKey);
       
   288         if (key == null) {
       
   289             throw new SignatureException("Missing key");
       
   290         }
       
   291         if (this.sigParams == null) {
       
   292             // Parameters are required for signature verification
       
   293             throw new SignatureException
       
   294                 ("Parameters required for RSASSA-PSS signatures");
       
   295         }
       
   296     }
       
   297 
       
   298     /**
       
   299      * Utility method for checking key length against digest length and
       
   300      * salt length
       
   301      */
       
   302     private static void checkKeyLength(RSAKey key, int digestLen,
       
   303             int saltLen) throws SignatureException {
       
   304         if (key != null) {
       
   305             int keyLength = getKeyLengthInBits(key) >> 3;
       
   306             int minLength = Math.addExact(Math.addExact(digestLen, saltLen), 2);
       
   307             if (keyLength < minLength) {
       
   308                 throw new SignatureException
       
   309                     ("Key is too short, need min " + minLength);
       
   310             }
       
   311         }
       
   312     }
       
   313 
       
   314     /**
       
   315      * Reset the message digest if it is not already reset.
       
   316      */
       
   317     private void resetDigest() {
       
   318         if (digestReset == false) {
       
   319             this.md.reset();
       
   320             digestReset = true;
       
   321         }
       
   322     }
       
   323 
       
   324     /**
       
   325      * Return the message digest value.
       
   326      */
       
   327     private byte[] getDigestValue() {
       
   328         digestReset = true;
       
   329         return this.md.digest();
       
   330     }
       
   331 
       
   332     // update the signature with the plaintext data. See JCA doc
       
   333     @Override
       
   334     protected void engineUpdate(byte b) throws SignatureException {
       
   335         ensureInit();
       
   336         this.md.update(b);
       
   337         digestReset = false;
       
   338     }
       
   339 
       
   340     // update the signature with the plaintext data. See JCA doc
       
   341     @Override
       
   342     protected void engineUpdate(byte[] b, int off, int len)
       
   343             throws SignatureException {
       
   344         ensureInit();
       
   345         this.md.update(b, off, len);
       
   346         digestReset = false;
       
   347     }
       
   348 
       
   349     // update the signature with the plaintext data. See JCA doc
       
   350     @Override
       
   351     protected void engineUpdate(ByteBuffer b) {
       
   352         try {
       
   353             ensureInit();
       
   354         } catch (SignatureException se) {
       
   355             // hack for working around API bug
       
   356             throw new RuntimeException(se.getMessage());
       
   357         }
       
   358         this.md.update(b);
       
   359         digestReset = false;
       
   360     }
       
   361 
       
   362     // sign the data and return the signature. See JCA doc
       
   363     @Override
       
   364     protected byte[] engineSign() throws SignatureException {
       
   365         ensureInit();
       
   366         byte[] mHash = getDigestValue();
       
   367         try {
       
   368             byte[] encoded = encodeSignature(mHash);
       
   369             byte[] encrypted = RSACore.rsa(encoded, privKey, true);
       
   370             return encrypted;
       
   371         } catch (GeneralSecurityException e) {
       
   372             throw new SignatureException("Could not sign data", e);
       
   373         } catch (IOException e) {
       
   374             throw new SignatureException("Could not encode data", e);
       
   375         }
       
   376     }
       
   377 
       
   378     // verify the data and return the result. See JCA doc
       
   379     // should be reset to the state after engineInitVerify call.
       
   380     @Override
       
   381     protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
       
   382         ensureInit();
       
   383         try {
       
   384             if (sigBytes.length != RSACore.getByteLength(this.pubKey)) {
       
   385                 throw new SignatureException
       
   386                     ("Signature length not correct: got "
       
   387                     + sigBytes.length + " but was expecting "
       
   388                     + RSACore.getByteLength(this.pubKey));
       
   389             }
       
   390             byte[] mHash = getDigestValue();
       
   391             byte[] decrypted = RSACore.rsa(sigBytes, this.pubKey);
       
   392             return decodeSignature(mHash, decrypted);
       
   393         } catch (javax.crypto.BadPaddingException e) {
       
   394             // occurs if the app has used the wrong RSA public key
       
   395             // or if sigBytes is invalid
       
   396             // return false rather than propagating the exception for
       
   397             // compatibility/ease of use
       
   398             return false;
       
   399         } catch (IOException e) {
       
   400             throw new SignatureException("Signature encoding error", e);
       
   401         } finally {
       
   402             resetDigest();
       
   403         }
       
   404     }
       
   405 
       
   406     // return the modulus length in bits
       
   407     private static int getKeyLengthInBits(RSAKey k) {
       
   408         if (k != null) {
       
   409             return k.getModulus().bitLength();
       
   410         }
       
   411         return -1;
       
   412     }
       
   413 
       
   414     /**
       
   415      * Encode the digest 'mHash', return the to-be-signed data.
       
   416      * Also used by the PKCS#11 provider.
       
   417      */
       
   418     private byte[] encodeSignature(byte[] mHash)
       
   419         throws IOException, DigestException {
       
   420         AlgorithmParameterSpec mgfParams = this.sigParams.getMGFParameters();
       
   421         String mgfDigestAlgo;
       
   422         if (mgfParams != null) {
       
   423             mgfDigestAlgo =
       
   424                 ((MGF1ParameterSpec) mgfParams).getDigestAlgorithm();
       
   425         } else {
       
   426             mgfDigestAlgo = this.md.getAlgorithm();
       
   427         }
       
   428         try {
       
   429             int emBits = getKeyLengthInBits(this.privKey) - 1;
       
   430             int emLen =(emBits + 7) >> 3;
       
   431             int hLen = this.md.getDigestLength();
       
   432             int dbLen = emLen - hLen - 1;
       
   433             int sLen = this.sigParams.getSaltLength();
       
   434 
       
   435             // maps DB into the corresponding region of EM and
       
   436             // stores its bytes directly into EM
       
   437             byte[] em = new byte[emLen];
       
   438 
       
   439             // step7 and some of step8
       
   440             em[dbLen - sLen - 1] = (byte) 1; // set DB's padding2 into EM
       
   441             em[em.length - 1] = (byte) 0xBC; // set trailer field of EM
       
   442 
       
   443             if (!digestReset) {
       
   444                 throw new ProviderException("Digest should be reset");
       
   445             }
       
   446             // step5: generates M' using padding1, mHash, and salt
       
   447             this.md.update(EIGHT_BYTES_OF_ZEROS);
       
   448             digestReset = false; // mark digest as it now has data
       
   449             this.md.update(mHash);
       
   450             if (sLen != 0) {
       
   451                 // step4: generate random salt
       
   452                 byte[] salt = new byte[sLen];
       
   453                 this.random.nextBytes(salt);
       
   454                 this.md.update(salt);
       
   455 
       
   456                 // step8: set DB's salt into EM
       
   457                 System.arraycopy(salt, 0, em, dbLen - sLen, sLen);
       
   458             }
       
   459             // step6: generate H using M'
       
   460             this.md.digest(em, dbLen, hLen); // set H field of EM
       
   461             digestReset = true;
       
   462 
       
   463             // step7 and 8 are already covered by the code which setting up
       
   464             // EM as above
       
   465 
       
   466             // step9 and 10: feed H into MGF and xor with DB in EM
       
   467             MGF1 mgf1 = new MGF1(mgfDigestAlgo);
       
   468             mgf1.generateAndXor(em, dbLen, hLen, dbLen, em, 0);
       
   469 
       
   470             // step11: set the leftmost (8emLen - emBits) bits of the leftmost
       
   471             // octet to 0
       
   472             int numZeroBits = (emLen << 3) - emBits;
       
   473             if (numZeroBits != 0) {
       
   474                 byte MASK = (byte) (0xff >>> numZeroBits);
       
   475                 em[0] = (byte) (em[0] & MASK);
       
   476             }
       
   477 
       
   478             // step12: em should now holds maskedDB || hash h || 0xBC
       
   479             return em;
       
   480         } catch (NoSuchAlgorithmException e) {
       
   481             throw new IOException(e.toString());
       
   482         }
       
   483     }
       
   484 
       
   485     /**
       
   486      * Decode the signature data. Verify that the object identifier matches
       
   487      * and return the message digest.
       
   488      */
       
   489     private boolean decodeSignature(byte[] mHash, byte[] em)
       
   490             throws IOException {
       
   491         int hLen = mHash.length;
       
   492         int sLen = this.sigParams.getSaltLength();
       
   493         int emLen = em.length;
       
   494         int emBits = getKeyLengthInBits(this.pubKey) - 1;
       
   495 
       
   496         // step3
       
   497         if (emLen < (hLen + sLen + 2)) {
       
   498             return false;
       
   499         }
       
   500 
       
   501         // step4
       
   502         if (em[emLen - 1] != (byte) 0xBC) {
       
   503             return false;
       
   504         }
       
   505 
       
   506         // step6: check if the leftmost (8emLen - emBits) bits of the leftmost
       
   507         // octet are 0
       
   508         int numZeroBits = (emLen << 3) - emBits;
       
   509         if (numZeroBits != 0) {
       
   510             byte MASK = (byte) (0xff << (8 - numZeroBits));
       
   511             if ((em[0] & MASK) != 0) {
       
   512                 return false;
       
   513             }
       
   514         }
       
   515         String mgfDigestAlgo;
       
   516         AlgorithmParameterSpec mgfParams = this.sigParams.getMGFParameters();
       
   517         if (mgfParams != null) {
       
   518             mgfDigestAlgo =
       
   519                 ((MGF1ParameterSpec) mgfParams).getDigestAlgorithm();
       
   520         } else {
       
   521             mgfDigestAlgo = this.md.getAlgorithm();
       
   522         }
       
   523         // step 7 and 8
       
   524         int dbLen = emLen - hLen - 1;
       
   525         try {
       
   526             MGF1 mgf1 = new MGF1(mgfDigestAlgo);
       
   527             mgf1.generateAndXor(em, dbLen, hLen, dbLen, em, 0);
       
   528         } catch (NoSuchAlgorithmException nsae) {
       
   529             throw new IOException(nsae.toString());
       
   530         }
       
   531 
       
   532         // step9: set the leftmost (8emLen - emBits) bits of the leftmost
       
   533         //  octet to 0
       
   534         if (numZeroBits != 0) {
       
   535             byte MASK = (byte) (0xff >>> numZeroBits);
       
   536             em[0] = (byte) (em[0] & MASK);
       
   537         }
       
   538 
       
   539         // step10
       
   540         int i = 0;
       
   541         for (; i < dbLen - sLen - 1; i++) {
       
   542             if (em[i] != 0) {
       
   543                 return false;
       
   544             }
       
   545         }
       
   546         if (em[i] != 0x01) {
       
   547             return false;
       
   548         }
       
   549         // step12 and 13
       
   550         this.md.update(EIGHT_BYTES_OF_ZEROS);
       
   551         digestReset = false;
       
   552         this.md.update(mHash);
       
   553         if (sLen > 0) {
       
   554             this.md.update(em, (dbLen - sLen), sLen);
       
   555         }
       
   556         byte[] digest2 = this.md.digest();
       
   557         digestReset = true;
       
   558 
       
   559         // step14
       
   560         byte[] digestInEM = Arrays.copyOfRange(em, dbLen, emLen - 1);
       
   561         return MessageDigest.isEqual(digest2, digestInEM);
       
   562     }
       
   563 
       
   564     // set parameter, not supported. See JCA doc
       
   565     @Deprecated
       
   566     @Override
       
   567     protected void engineSetParameter(String param, Object value)
       
   568             throws InvalidParameterException {
       
   569         throw new UnsupportedOperationException("setParameter() not supported");
       
   570     }
       
   571 
       
   572     @Override
       
   573     protected void engineSetParameter(AlgorithmParameterSpec params)
       
   574             throws InvalidAlgorithmParameterException {
       
   575         this.sigParams = validateSigParams(params);
       
   576         // disallow changing parameters when digest has been used
       
   577         if (!digestReset) {
       
   578             throw new ProviderException
       
   579                 ("Cannot set parameters during operations");
       
   580         }
       
   581         String newHashAlg = this.sigParams.getDigestAlgorithm();
       
   582         // re-allocate md if not yet assigned or algorithm changed
       
   583         if ((this.md == null) ||
       
   584             !(this.md.getAlgorithm().equalsIgnoreCase(newHashAlg))) {
       
   585             try {
       
   586                 this.md = MessageDigest.getInstance(newHashAlg);
       
   587             } catch (NoSuchAlgorithmException nsae) {
       
   588                 // should not happen as we pick default digest algorithm
       
   589                 throw new InvalidAlgorithmParameterException
       
   590                     ("Unsupported digest algorithm " +
       
   591                      newHashAlg, nsae);
       
   592             }
       
   593         }
       
   594     }
       
   595 
       
   596     // get parameter, not supported. See JCA doc
       
   597     @Deprecated
       
   598     @Override
       
   599     protected Object engineGetParameter(String param)
       
   600             throws InvalidParameterException {
       
   601         throw new UnsupportedOperationException("getParameter() not supported");
       
   602     }
       
   603 
       
   604     @Override
       
   605     protected AlgorithmParameters engineGetParameters() {
       
   606         if (this.sigParams == null) {
       
   607             throw new ProviderException("Missing required PSS parameters");
       
   608         }
       
   609         try {
       
   610             AlgorithmParameters ap =
       
   611                 AlgorithmParameters.getInstance("RSASSA-PSS");
       
   612             ap.init(this.sigParams);
       
   613             return ap;
       
   614         } catch (GeneralSecurityException gse) {
       
   615             throw new ProviderException(gse.getMessage());
       
   616         }
       
   617     }
       
   618 }