changeset 54483:ac20c3bdc55d

8216039: TLS with BC and RSASSA-PSS breaks ECDHServerKeyExchange Summary: Add internal Signature init methods to select provider based on both key and parameter Reviewed-by: xuelei
author valeriep
date Wed, 10 Apr 2019 02:35:18 +0000
parents f847a42ddc01
children 72f05350b4b3
files src/java.base/share/classes/java/security/Signature.java src/java.base/share/classes/java/security/SignatureSpi.java src/java.base/share/classes/java/security/cert/X509CRL.java src/java.base/share/classes/java/security/cert/X509Certificate.java src/java.base/share/classes/jdk/internal/access/JavaSecuritySignatureAccess.java src/java.base/share/classes/jdk/internal/access/SharedSecrets.java src/java.base/share/classes/sun/security/pkcs/SignerInfo.java src/java.base/share/classes/sun/security/pkcs10/PKCS10.java src/java.base/share/classes/sun/security/ssl/SignatureScheme.java src/java.base/share/classes/sun/security/tools/keytool/Main.java src/java.base/share/classes/sun/security/util/SignatureUtil.java src/java.base/share/classes/sun/security/x509/X509CRLImpl.java src/java.base/share/classes/sun/security/x509/X509CertImpl.java test/jdk/java/security/Signature/SignatureGetInstance.java test/jdk/sun/security/util/misc/SetNullSigParams.java
diffstat 15 files changed, 721 insertions(+), 175 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/security/Signature.java	Tue Apr 09 16:32:22 2019 -0700
+++ b/src/java.base/share/classes/java/security/Signature.java	Wed Apr 10 02:35:18 2019 +0000
@@ -40,6 +40,8 @@
 import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.BadPaddingException;
 import javax.crypto.NoSuchPaddingException;
+import jdk.internal.access.JavaSecuritySignatureAccess;
+import jdk.internal.access.SharedSecrets;
 
 import sun.security.util.Debug;
 import sun.security.jca.*;
@@ -118,6 +120,34 @@
 
 public abstract class Signature extends SignatureSpi {
 
+    static {
+        SharedSecrets.setJavaSecuritySignatureAccess(
+            new JavaSecuritySignatureAccess() {
+                @Override
+                public void initVerify(Signature s, PublicKey publicKey,
+                        AlgorithmParameterSpec params)
+                        throws InvalidKeyException,
+                        InvalidAlgorithmParameterException {
+                    s.initVerify(publicKey, params);
+                }
+                @Override
+                public void initVerify(Signature s,
+                        java.security.cert.Certificate certificate,
+                        AlgorithmParameterSpec params)
+                        throws InvalidKeyException,
+                        InvalidAlgorithmParameterException {
+                    s.initVerify(certificate, params);
+                }
+                @Override
+                public void initSign(Signature s, PrivateKey privateKey,
+                        AlgorithmParameterSpec params, SecureRandom random)
+                        throws InvalidKeyException,
+                        InvalidAlgorithmParameterException {
+                    s.initSign(privateKey, params, random);
+                }
+        });
+    }
+
     private static final Debug debug =
                         Debug.getInstance("jca", "Signature");
 
@@ -482,6 +512,53 @@
     }
 
     /**
+     * Initialize this object for verification. If this method is called
+     * again with different arguments, it negates the effect
+     * of this call.
+     *
+     * @param publicKey the public key of the identity whose signature is
+     * going to be verified.
+     * @param params the parameters used for verifying this signature.
+     *
+     * @exception InvalidKeyException if the key is invalid.
+     * @exception InvalidAlgorithmParameterException if the params is invalid.
+     */
+    final void initVerify(PublicKey publicKey, AlgorithmParameterSpec params)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        engineInitVerify(publicKey, params);
+        state = VERIFY;
+
+        if (!skipDebug && pdebug != null) {
+            pdebug.println("Signature." + algorithm +
+                " verification algorithm from: " + getProviderName());
+        }
+    }
+
+    private static PublicKey getPublicKeyFromCert(Certificate cert)
+            throws InvalidKeyException {
+        // If the certificate is of type X509Certificate,
+        // we should check whether it has a Key Usage
+        // extension marked as critical.
+        //if (cert instanceof java.security.cert.X509Certificate) {
+        if (cert instanceof X509Certificate) {
+            // Check whether the cert has a key usage extension
+            // marked as a critical extension.
+            // The OID for KeyUsage extension is 2.5.29.15.
+            X509Certificate c = (X509Certificate)cert;
+            Set<String> critSet = c.getCriticalExtensionOIDs();
+
+            if (critSet != null && !critSet.isEmpty()
+                && critSet.contains("2.5.29.15")) {
+                boolean[] keyUsageInfo = c.getKeyUsage();
+                // keyUsageInfo[0] is for digitalSignature.
+                if ((keyUsageInfo != null) && (keyUsageInfo[0] == false))
+                    throw new InvalidKeyException("Wrong key usage");
+            }
+        }
+        return cert.getPublicKey();
+    }
+
+    /**
      * Initializes this object for verification, using the public key from
      * the given certificate.
      * <p>If the certificate is of type X.509 and has a <i>key usage</i>
@@ -501,27 +578,40 @@
      */
     public final void initVerify(Certificate certificate)
             throws InvalidKeyException {
-        // If the certificate is of type X509Certificate,
-        // we should check whether it has a Key Usage
-        // extension marked as critical.
-        if (certificate instanceof java.security.cert.X509Certificate) {
-            // Check whether the cert has a key usage extension
-            // marked as a critical extension.
-            // The OID for KeyUsage extension is 2.5.29.15.
-            X509Certificate cert = (X509Certificate)certificate;
-            Set<String> critSet = cert.getCriticalExtensionOIDs();
+        engineInitVerify(getPublicKeyFromCert(certificate));
+        state = VERIFY;
 
-            if (critSet != null && !critSet.isEmpty()
-                && critSet.contains("2.5.29.15")) {
-                boolean[] keyUsageInfo = cert.getKeyUsage();
-                // keyUsageInfo[0] is for digitalSignature.
-                if ((keyUsageInfo != null) && (keyUsageInfo[0] == false))
-                    throw new InvalidKeyException("Wrong key usage");
-            }
+        if (!skipDebug && pdebug != null) {
+            pdebug.println("Signature." + algorithm +
+                " verification algorithm from: " + getProviderName());
         }
+    }
 
-        PublicKey publicKey = certificate.getPublicKey();
-        engineInitVerify(publicKey);
+    /**
+     * Initializes this object for verification, using the public key from
+     * the given certificate.
+     * <p>If the certificate is of type X.509 and has a <i>key usage</i>
+     * extension field marked as critical, and the value of the <i>key usage</i>
+     * extension field implies that the public key in
+     * the certificate and its corresponding private key are not
+     * supposed to be used for digital signatures, an
+     * {@code InvalidKeyException} is thrown.
+     *
+     * @param certificate the certificate of the identity whose signature is
+     * going to be verified.
+     * @param params the parameters used for verifying this signature.
+     *
+     * @exception InvalidKeyException  if the public key in the certificate
+     * is not encoded properly or does not include required  parameter
+     * information or cannot be used for digital signature purposes.
+     * @exception InvalidAlgorithmParameterException if the params is invalid.
+     *
+     * @since 13
+     */
+    final void initVerify(Certificate certificate,
+            AlgorithmParameterSpec params)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        engineInitVerify(getPublicKeyFromCert(certificate), params);
         state = VERIFY;
 
         if (!skipDebug && pdebug != null) {
@@ -575,6 +665,31 @@
     }
 
     /**
+     * Initialize this object for signing. If this method is called
+     * again with different arguments, it negates the effect
+     * of this call.
+     *
+     * @param privateKey the private key of the identity whose signature
+     * is going to be generated.
+     * @param params the parameters used for generating signature.
+     * @param random the source of randomness for this signature.
+     *
+     * @exception InvalidKeyException if the key is invalid.
+     * @exception InvalidAlgorithmParameterException if the params is invalid
+     */
+    final void initSign(PrivateKey privateKey,
+            AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        engineInitSign(privateKey, params, random);
+        state = SIGN;
+
+        if (!skipDebug && pdebug != null) {
+            pdebug.println("Signature." + algorithm +
+                " signing algorithm from: " + getProviderName());
+        }
+    }
+
+    /**
      * Returns the signature bytes of all the data updated.
      * The format of the signature depends on the underlying
      * signature scheme.
@@ -1110,11 +1225,13 @@
             }
         }
 
-        private void chooseProvider(int type, Key key, SecureRandom random)
-                throws InvalidKeyException {
+        // Used by engineSetParameter/engineInitSign/engineInitVerify() to
+        // find the right provider with the supplied key, parameters, random source
+        private void chooseProvider(int type, Key key,
+                AlgorithmParameterSpec params, SecureRandom random)
+                throws InvalidKeyException, InvalidAlgorithmParameterException {
             synchronized (lock) {
                 if (sigSpi != null) {
-                    init(sigSpi, type, key, random);
                     return;
                 }
                 Exception lastException = null;
@@ -1127,7 +1244,7 @@
                         s = serviceIterator.next();
                     }
                     // if provider says it does not support this key, ignore it
-                    if (s.supportsParameter(key) == false) {
+                    if (key != null && s.supportsParameter(key) == false) {
                         continue;
                     }
                     // if instance is not a SignatureSpi, ignore it
@@ -1136,7 +1253,7 @@
                     }
                     try {
                         SignatureSpi spi = newInstance(s);
-                        init(spi, type, key, random);
+                        tryOperation(spi, type, key, params, random);
                         provider = s.getProvider();
                         sigSpi = spi;
                         firstService = null;
@@ -1158,6 +1275,10 @@
                 if (lastException instanceof RuntimeException) {
                     throw (RuntimeException)lastException;
                 }
+                if (lastException instanceof InvalidAlgorithmParameterException) {
+                    throw (InvalidAlgorithmParameterException)lastException;
+                }
+
                 String k = (key != null) ? key.getClass().getName() : "(null)";
                 throw new InvalidKeyException
                     ("No installed provider supports this key: "
@@ -1165,22 +1286,35 @@
             }
         }
 
-        private static final int I_PUB     = 1;
-        private static final int I_PRIV    = 2;
-        private static final int I_PRIV_SR = 3;
+        private static final int I_PUB           = 1;
+        private static final int I_PRIV          = 2;
+        private static final int I_PRIV_SR       = 3;
+        private static final int I_PUB_PARAM     = 4;
+        private static final int I_PRIV_PARAM_SR = 5;
+        private static final int S_PARAM         = 6;
 
-        private void init(SignatureSpi spi, int type, Key  key,
-                SecureRandom random) throws InvalidKeyException {
+        private void tryOperation(SignatureSpi spi, int type, Key  key,
+                AlgorithmParameterSpec params, SecureRandom random)
+                throws InvalidKeyException, InvalidAlgorithmParameterException {
             switch (type) {
             case I_PUB:
                 spi.engineInitVerify((PublicKey)key);
                 break;
+            case I_PUB_PARAM:
+                spi.engineInitVerify((PublicKey)key, params);
+                break;
             case I_PRIV:
                 spi.engineInitSign((PrivateKey)key);
                 break;
             case I_PRIV_SR:
                 spi.engineInitSign((PrivateKey)key, random);
                 break;
+            case I_PRIV_PARAM_SR:
+                spi.engineInitSign((PrivateKey)key, params, random);
+                break;
+            case S_PARAM:
+                spi.engineSetParameter(params);
+                break;
             default:
                 throw new AssertionError("Internal error: " + type);
             }
@@ -1191,7 +1325,22 @@
             if (sigSpi != null) {
                 sigSpi.engineInitVerify(publicKey);
             } else {
-                chooseProvider(I_PUB, publicKey, null);
+                try {
+                    chooseProvider(I_PUB, publicKey, null, null);
+                } catch (InvalidAlgorithmParameterException iape) {
+                    // should not happen, re-throw as IKE just in case
+                    throw new InvalidKeyException(iape);
+                }
+            }
+        }
+
+        void engineInitVerify(PublicKey publicKey,
+                AlgorithmParameterSpec params)
+                throws InvalidKeyException, InvalidAlgorithmParameterException {
+            if (sigSpi != null) {
+                sigSpi.engineInitVerify(publicKey, params);
+            } else {
+                chooseProvider(I_PUB_PARAM, publicKey, params, null);
             }
         }
 
@@ -1200,7 +1349,12 @@
             if (sigSpi != null) {
                 sigSpi.engineInitSign(privateKey);
             } else {
-                chooseProvider(I_PRIV, privateKey, null);
+                try {
+                    chooseProvider(I_PRIV, privateKey, null, null);
+                } catch (InvalidAlgorithmParameterException iape) {
+                    // should not happen, re-throw as IKE just in case
+                    throw new InvalidKeyException(iape);
+                }
             }
         }
 
@@ -1209,7 +1363,22 @@
             if (sigSpi != null) {
                 sigSpi.engineInitSign(privateKey, sr);
             } else {
-                chooseProvider(I_PRIV_SR, privateKey, sr);
+                try {
+                    chooseProvider(I_PRIV_SR, privateKey, null, sr);
+                } catch (InvalidAlgorithmParameterException iape) {
+                    // should not happen, re-throw as IKE just in case
+                    throw new InvalidKeyException(iape);
+                }
+            }
+        }
+
+        void engineInitSign(PrivateKey privateKey,
+                AlgorithmParameterSpec params, SecureRandom sr)
+                throws InvalidKeyException, InvalidAlgorithmParameterException {
+            if (sigSpi != null) {
+                sigSpi.engineInitSign(privateKey, params, sr);
+            } else {
+                chooseProvider(I_PRIV_PARAM_SR, privateKey, params, sr);
             }
         }
 
@@ -1260,8 +1429,16 @@
 
         protected void engineSetParameter(AlgorithmParameterSpec params)
                 throws InvalidAlgorithmParameterException {
-            chooseFirstProvider();
-            sigSpi.engineSetParameter(params);
+            if (sigSpi != null) {
+                sigSpi.engineSetParameter(params);
+            } else {
+                try {
+                    chooseProvider(S_PARAM, null, params, null);
+                } catch (InvalidKeyException ike) {
+                    // should never happen, rethrow just in case
+                    throw new InvalidAlgorithmParameterException(ike);
+                }
+            }
         }
 
         protected Object engineGetParameter(String param)
--- a/src/java.base/share/classes/java/security/SignatureSpi.java	Tue Apr 09 16:32:22 2019 -0700
+++ b/src/java.base/share/classes/java/security/SignatureSpi.java	Wed Apr 10 02:35:18 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -72,6 +72,33 @@
 
     /**
      * Initializes this signature object with the specified
+     * public key for verification operations.
+     *
+     * @param publicKey the public key of the identity whose signature is
+     * going to be verified.
+     * @param params the parameters for generating this signature
+     *
+     * @exception InvalidKeyException if the key is improperly
+     * encoded, does not work with the given parameters, and so on.
+     * @exception InvalidAlgorithmParameterException if the given parameters
+     * is invalid.
+     */
+    void engineInitVerify(PublicKey publicKey,
+            AlgorithmParameterSpec params)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        if (params != null) {
+            try {
+                engineSetParameter(params);
+            } catch (UnsupportedOperationException usoe) {
+                // error out if not overrridden
+                throw new InvalidAlgorithmParameterException(usoe);
+            }
+        }
+        engineInitVerify(publicKey);
+    }
+
+    /**
+     * Initializes this signature object with the specified
      * private key for signing operations.
      *
      * @param privateKey the private key of the identity whose signature
@@ -98,10 +125,41 @@
      * encoded, parameters are missing, and so on.
      */
     protected void engineInitSign(PrivateKey privateKey,
-                                  SecureRandom random)
-        throws InvalidKeyException {
-            this.appRandom = random;
-            engineInitSign(privateKey);
+            SecureRandom random)
+            throws InvalidKeyException {
+        this.appRandom = random;
+        engineInitSign(privateKey);
+    }
+
+    /**
+     * Initializes this signature object with the specified
+     * private key and source of randomness for signing operations.
+     *
+     * <p>This concrete method has been added to this previously-defined
+     * abstract class. (For backwards compatibility, it cannot be abstract.)
+     *
+     * @param privateKey the private key of the identity whose signature
+     * will be generated.
+     * @param params the parameters for generating this signature
+     * @param random the source of randomness
+     *
+     * @exception InvalidKeyException if the key is improperly
+     * encoded, parameters are missing, and so on.
+     * @exception InvalidAlgorithmParameterException if the parameters is
+     * invalid.
+     */
+    void engineInitSign(PrivateKey privateKey,
+            AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        if (params != null) {
+            try {
+                engineSetParameter(params);
+            } catch (UnsupportedOperationException usoe) {
+                // error out if not overrridden
+                throw new InvalidAlgorithmParameterException(usoe);
+            }
+        }
+        engineInitSign(privateKey, random);
     }
 
     /**
@@ -127,7 +185,7 @@
      * properly
      */
     protected abstract void engineUpdate(byte[] b, int off, int len)
-        throws SignatureException;
+            throws SignatureException;
 
     /**
      * Updates the data to be signed or verified using the specified
@@ -223,7 +281,7 @@
      * @since 1.2
      */
     protected int engineSign(byte[] outbuf, int offset, int len)
-                        throws SignatureException {
+             throws SignatureException {
         byte[] sig = engineSign();
         if (len < sig.length) {
                 throw new SignatureException
@@ -251,7 +309,7 @@
      * process the input data provided, etc.
      */
     protected abstract boolean engineVerify(byte[] sigBytes)
-        throws SignatureException;
+            throws SignatureException;
 
     /**
      * Verifies the passed-in signature in the specified array
@@ -273,7 +331,7 @@
      * @since 1.4
      */
     protected boolean engineVerify(byte[] sigBytes, int offset, int length)
-        throws SignatureException {
+            throws SignatureException {
         byte[] sigBytesCopy = new byte[length];
         System.arraycopy(sigBytes, offset, sigBytesCopy, 0, length);
         return engineVerify(sigBytesCopy);
@@ -305,7 +363,7 @@
      */
     @Deprecated
     protected abstract void engineSetParameter(String param, Object value)
-        throws InvalidParameterException;
+            throws InvalidParameterException;
 
     /**
      * <p>This method is overridden by providers to initialize
@@ -321,8 +379,8 @@
      * are inappropriate for this signature engine
      */
     protected void engineSetParameter(AlgorithmParameterSpec params)
-        throws InvalidAlgorithmParameterException {
-            throw new UnsupportedOperationException();
+            throws InvalidAlgorithmParameterException {
+        throw new UnsupportedOperationException();
     }
 
     /**
--- a/src/java.base/share/classes/java/security/cert/X509CRL.java	Tue Apr 09 16:32:22 2019 -0700
+++ b/src/java.base/share/classes/java/security/cert/X509CRL.java	Wed Apr 10 02:35:18 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -239,16 +239,15 @@
     public void verify(PublicKey key, Provider sigProvider)
         throws CRLException, NoSuchAlgorithmException,
         InvalidKeyException, SignatureException {
+        String sigAlgName = getSigAlgName();
         Signature sig = (sigProvider == null)
-            ? Signature.getInstance(getSigAlgName())
-            : Signature.getInstance(getSigAlgName(), sigProvider);
+            ? Signature.getInstance(sigAlgName)
+            : Signature.getInstance(sigAlgName, sigProvider);
 
-        sig.initVerify(key);
-
-        // set parameters after Signature.initSign/initVerify call,
-        // so the deferred provider selections occur when key is set
         try {
-            SignatureUtil.specialSetParameter(sig, getSigAlgParams());
+            byte[] paramBytes = getSigAlgParams();
+            SignatureUtil.initVerifyWithParam(sig, key,
+                SignatureUtil.getParamSpec(sigAlgName, paramBytes));
         } catch (ProviderException e) {
             throw new CRLException(e.getMessage(), e.getCause());
         } catch (InvalidAlgorithmParameterException e) {
--- a/src/java.base/share/classes/java/security/cert/X509Certificate.java	Tue Apr 09 16:32:22 2019 -0700
+++ b/src/java.base/share/classes/java/security/cert/X509Certificate.java	Wed Apr 10 02:35:18 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -676,16 +676,14 @@
     public void verify(PublicKey key, Provider sigProvider)
         throws CertificateException, NoSuchAlgorithmException,
         InvalidKeyException, SignatureException {
+        String sigName = getSigAlgName();
         Signature sig = (sigProvider == null)
-            ? Signature.getInstance(getSigAlgName())
-            : Signature.getInstance(getSigAlgName(), sigProvider);
+            ? Signature.getInstance(sigName)
+            : Signature.getInstance(sigName, sigProvider);
 
-        sig.initVerify(key);
-
-        // set parameters after Signature.initSign/initVerify call,
-        // so the deferred provider selections occur when key is set
         try {
-            SignatureUtil.specialSetParameter(sig, getSigAlgParams());
+            SignatureUtil.initVerifyWithParam(sig, key,
+                SignatureUtil.getParamSpec(sigName, getSigAlgParams()));
         } catch (ProviderException e) {
             throw new CertificateException(e.getMessage(), e.getCause());
         } catch (InvalidAlgorithmParameterException e) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/access/JavaSecuritySignatureAccess.java	Wed Apr 10 02:35:18 2019 +0000
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.access;
+
+import java.security.*;
+import java.security.spec.AlgorithmParameterSpec;
+
+public interface JavaSecuritySignatureAccess {
+
+    void initVerify(Signature s, PublicKey publicKey, AlgorithmParameterSpec params)
+            throws InvalidKeyException, InvalidAlgorithmParameterException;
+
+    void initVerify(Signature s, java.security.cert.Certificate certificate,
+             AlgorithmParameterSpec params)
+             throws InvalidKeyException, InvalidAlgorithmParameterException;
+
+    void initSign(Signature s, PrivateKey privateKey,
+             AlgorithmParameterSpec params, SecureRandom random)
+             throws InvalidKeyException, InvalidAlgorithmParameterException;
+}
--- a/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java	Tue Apr 09 16:32:22 2019 -0700
+++ b/src/java.base/share/classes/jdk/internal/access/SharedSecrets.java	Wed Apr 10 02:35:18 2019 +0000
@@ -36,6 +36,7 @@
 import java.io.ObjectInputStream;
 import java.io.RandomAccessFile;
 import java.security.ProtectionDomain;
+import java.security.Signature;
 import jdk.internal.misc.Unsafe;
 
 /** A repository of "shared secrets", which are a mechanism for
@@ -73,6 +74,7 @@
     private static JavaObjectInputStreamAccess javaObjectInputStreamAccess;
     private static JavaObjectInputFilterAccess javaObjectInputFilterAccess;
     private static JavaIORandomAccessFileAccess javaIORandomAccessFileAccess;
+    private static JavaSecuritySignatureAccess javaSecuritySignatureAccess;
     private static JavaxCryptoSealedObjectAccess javaxCryptoSealedObjectAccess;
 
     public static JavaUtilJarAccess javaUtilJarAccess() {
@@ -327,6 +329,17 @@
         return javaIORandomAccessFileAccess;
     }
 
+    public static void setJavaSecuritySignatureAccess(JavaSecuritySignatureAccess jssa) {
+        javaSecuritySignatureAccess = jssa;
+    }
+
+    public static JavaSecuritySignatureAccess getJavaSecuritySignatureAccess() {
+        if (javaSecuritySignatureAccess == null) {
+            unsafe.ensureClassInitialized(Signature.class);
+        }
+        return javaSecuritySignatureAccess;
+    }
+
     public static void setJavaxCryptoSealedObjectAccess(JavaxCryptoSealedObjectAccess jcsoa) {
         javaxCryptoSealedObjectAccess = jcsoa;
     }
--- a/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java	Tue Apr 09 16:32:22 2019 -0700
+++ b/src/java.base/share/classes/sun/security/pkcs/SignerInfo.java	Wed Apr 10 02:35:18 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -447,15 +447,13 @@
 
             Signature sig = Signature.getInstance(algname);
 
-            sig.initVerify(key);
-
-            // set parameters after Signature.initSign/initVerify call,
-            // so the deferred provider selections occur when key is set
             AlgorithmParameters ap =
                 digestEncryptionAlgorithmId.getParameters();
             try {
-                SignatureUtil.specialSetParameter(sig, ap);
-            } catch (ProviderException | InvalidAlgorithmParameterException e) {
+                SignatureUtil.initVerifyWithParam(sig, key,
+                    SignatureUtil.getParamSpec(algname, ap));
+            } catch (ProviderException | InvalidAlgorithmParameterException |
+                     InvalidKeyException e) {
                 throw new SignatureException(e.getMessage(), e);
             }
 
@@ -466,8 +464,6 @@
         } catch (IOException e) {
             throw new SignatureException("IO error verifying signature:\n" +
                                          e.getMessage());
-        } catch (InvalidKeyException e) {
-            throw new SignatureException("InvalidKey: " + e.getMessage());
         }
         return null;
     }
--- a/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java	Tue Apr 09 16:32:22 2019 -0700
+++ b/src/java.base/share/classes/sun/security/pkcs10/PKCS10.java	Wed Apr 10 02:35:18 2019 +0000
@@ -167,12 +167,8 @@
         try {
             sigAlg = id.getName();
             sig = Signature.getInstance(sigAlg);
-
-            sig.initVerify(subjectPublicKeyInfo);
-
-            // set parameters after Signature.initSign/initVerify call,
-            // so the deferred provider selections occur when key is set
-            SignatureUtil.specialSetParameter(sig, id.getParameters());
+            SignatureUtil.initVerifyWithParam(sig, subjectPublicKeyInfo,
+                SignatureUtil.getParamSpec(sigAlg, id.getParameters()));
 
             sig.update(data);
             if (!sig.verify(sigData)) {
--- a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java	Tue Apr 09 16:32:22 2019 -0700
+++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java	Wed Apr 10 02:35:18 2019 +0000
@@ -43,6 +43,7 @@
 import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
 import sun.security.ssl.X509Authentication.X509Possession;
 import sun.security.util.KeyUtil;
+import sun.security.util.SignatureUtil;
 
 enum SignatureScheme {
     // EdDSA algorithms
@@ -471,16 +472,11 @@
 
         Signature signer = Signature.getInstance(algorithm);
         if (key instanceof PublicKey) {
-            signer.initVerify((PublicKey)(key));
+            SignatureUtil.initVerifyWithParam(signer, (PublicKey)key,
+                    signAlgParameter);
         } else {
-            signer.initSign((PrivateKey)key);
-        }
-
-        // Important note:  Please don't set the parameters before signature
-        // or verification initialization, so that the crypto provider can
-        // be selected properly.
-        if (signAlgParameter != null) {
-            signer.setParameter(signAlgParameter);
+            SignatureUtil.initSignWithParam(signer, (PrivateKey)key,
+                    signAlgParameter, null);
         }
 
         return signer;
--- a/src/java.base/share/classes/sun/security/tools/keytool/Main.java	Tue Apr 09 16:32:22 2019 -0700
+++ b/src/java.base/share/classes/sun/security/tools/keytool/Main.java	Wed Apr 10 02:35:18 2019 +0000
@@ -84,6 +84,7 @@
 import sun.security.util.Password;
 import sun.security.util.SecurityProperties;
 import sun.security.util.SecurityProviderConstants;
+import sun.security.util.SignatureUtil;
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
 import javax.crypto.SecretKeyFactory;
@@ -1441,11 +1442,12 @@
             sigAlgName = getCompatibleSigAlgName(privateKey);
         }
         Signature signature = Signature.getInstance(sigAlgName);
-        signature.initSign(privateKey);
-
-        X509CertInfo info = new X509CertInfo();
         AlgorithmParameterSpec params = AlgorithmId
                 .getDefaultAlgorithmParameterSpec(sigAlgName, privateKey);
+
+        SignatureUtil.initSignWithParam(signature, privateKey, params, null);
+
+        X509CertInfo info = new X509CertInfo();
         AlgorithmId algID = AlgorithmId.getWithParameterSpec(sigAlgName, params);
         info.set(X509CertInfo.VALIDITY, interval);
         info.set(X509CertInfo.SERIAL_NUMBER,
@@ -1599,12 +1601,9 @@
         }
 
         Signature signature = Signature.getInstance(sigAlgName);
-        signature.initSign(privKey);
         AlgorithmParameterSpec params = AlgorithmId
                 .getDefaultAlgorithmParameterSpec(sigAlgName, privKey);
-        if (params != null) {
-            signature.setParameter(params);
-        }
+        SignatureUtil.initSignWithParam(signature, privKey, params, null);
 
         X500Name subject = dname == null?
                 new X500Name(((X509Certificate)cert).getSubjectDN().toString()):
--- a/src/java.base/share/classes/sun/security/util/SignatureUtil.java	Tue Apr 09 16:32:22 2019 -0700
+++ b/src/java.base/share/classes/sun/security/util/SignatureUtil.java	Wed Apr 10 02:35:18 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,6 +29,7 @@
 import java.security.*;
 import java.security.spec.*;
 import sun.security.rsa.RSAUtil;
+import jdk.internal.access.SharedSecrets;
 
 /**
  * Utility class for Signature related operations. Currently used by various
@@ -39,12 +40,25 @@
  */
 public class SignatureUtil {
 
+    private static String checkName(String algName) throws ProviderException {
+        if (algName.indexOf(".") == -1) {
+            return algName;
+        }
+        // convert oid to String
+        try {
+            return Signature.getInstance(algName).getAlgorithm();
+        } catch (Exception e) {
+            throw new ProviderException("Error mapping algorithm name", e);
+        }
+    }
+
     // Utility method of creating an AlgorithmParameters object with
     // the specified algorithm name and encoding
     private static AlgorithmParameters createAlgorithmParameters(String algName,
             byte[] paramBytes) throws ProviderException {
 
         try {
+            algName = checkName(algName);
             AlgorithmParameters result =
                 AlgorithmParameters.getInstance(algName);
             result.init(paramBytes);
@@ -54,52 +68,81 @@
         }
     }
 
-    private static AlgorithmParameterSpec getParamSpec(String sigName,
+    // Utility method for converting the specified AlgorithmParameters object
+    // into an AlgorithmParameterSpec object.
+    public static AlgorithmParameterSpec getParamSpec(String sigName,
             AlgorithmParameters params)
-            throws InvalidAlgorithmParameterException, ProviderException {
+            throws ProviderException {
 
-        if (params == null) return null;
-
-        if (sigName.toUpperCase().indexOf("RSA") == -1) {
-            throw new ProviderException
-                 ("Unrecognized algorithm for signature parameters " +
-                  sigName);
+        sigName = checkName(sigName);
+        AlgorithmParameterSpec paramSpec = null;
+        if (params != null) {
+            if (sigName.toUpperCase().indexOf("RSA") == -1) {
+                throw new ProviderException
+                    ("Unrecognized algorithm for signature parameters " +
+                     sigName);
+            }
+            // AlgorithmParameters.getAlgorithm() may returns oid if it's
+            // created during DER decoding. Convert to use the standard name
+            // before passing it to RSAUtil
+            if (params.getAlgorithm().indexOf(".") != -1) {
+                try {
+                    params = createAlgorithmParameters(sigName,
+                        params.getEncoded());
+                } catch (IOException e) {
+                    throw new ProviderException(e);
+                }
+            }
+            paramSpec = RSAUtil.getParamSpec(params);
         }
-        // AlgorithmParameters.getAlgorithm() may returns oid if it's
-        // created during DER decoding. Convert to use the standard name
-        // before passing it to RSAUtil
-        String alg = params.getAlgorithm();
-        if (alg.equalsIgnoreCase(sigName) || alg.indexOf(".") != -1) {
-            try {
-                params = createAlgorithmParameters(sigName,
-                    params.getEncoded());
-            } catch (IOException e) {
-                throw new ProviderException(e);
-            }
-        }
-        return RSAUtil.getParamSpec(params);
+        return paramSpec;
     }
 
-    // Special method for setting the specified parameter bytes into the
-    // specified Signature object as signature parameters.
-    public static void specialSetParameter(Signature sig, byte[] paramBytes)
-            throws InvalidAlgorithmParameterException, ProviderException {
+    // Utility method for converting the specified parameter bytes into an
+    // AlgorithmParameterSpec object.
+    public static AlgorithmParameterSpec getParamSpec(String sigName,
+            byte[] paramBytes)
+            throws ProviderException {
+        sigName = checkName(sigName);
+        AlgorithmParameterSpec paramSpec = null;
         if (paramBytes != null) {
-            String sigName = sig.getAlgorithm();
+            if (sigName.toUpperCase().indexOf("RSA") == -1) {
+                throw new ProviderException
+                     ("Unrecognized algorithm for signature parameters " +
+                      sigName);
+            }
             AlgorithmParameters params =
                 createAlgorithmParameters(sigName, paramBytes);
-            specialSetParameter(sig, params);
+            paramSpec = RSAUtil.getParamSpec(params);
         }
+        return paramSpec;
     }
 
-    // Special method for setting the specified AlgorithmParameter object
-    // into the specified Signature object as signature parameters.
-    public static void specialSetParameter(Signature sig,
-            AlgorithmParameters params)
-            throws InvalidAlgorithmParameterException, ProviderException {
-        if (params != null) {
-            String sigName = sig.getAlgorithm();
-            sig.setParameter(getParamSpec(sigName, params));
-        }
+    // Utility method for initializing the specified Signature object
+    // for verification with the specified key and params (may be null)
+    public static void initVerifyWithParam(Signature s, PublicKey key,
+            AlgorithmParameterSpec params)
+            throws ProviderException, InvalidAlgorithmParameterException,
+            InvalidKeyException {
+        SharedSecrets.getJavaSecuritySignatureAccess().initVerify(s, key, params);
+    }
+
+    // Utility method for initializing the specified Signature object
+    // for verification with the specified Certificate and params (may be null)
+    public static void initVerifyWithParam(Signature s,
+            java.security.cert.Certificate cert,
+            AlgorithmParameterSpec params)
+            throws ProviderException, InvalidAlgorithmParameterException,
+            InvalidKeyException {
+        SharedSecrets.getJavaSecuritySignatureAccess().initVerify(s, cert, params);
+    }
+
+    // Utility method for initializing the specified Signature object
+    // for signing with the specified key and params (may be null)
+    public static void initSignWithParam(Signature s, PrivateKey key,
+            AlgorithmParameterSpec params, SecureRandom sr)
+            throws ProviderException, InvalidAlgorithmParameterException,
+            InvalidKeyException {
+        SharedSecrets.getJavaSecuritySignatureAccess().initSign(s, key, params, sr);
     }
 }
--- a/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java	Tue Apr 09 16:32:22 2019 -0700
+++ b/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java	Wed Apr 10 02:35:18 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -370,18 +370,16 @@
             throw new CRLException("Uninitialized CRL");
         }
         Signature   sigVerf = null;
+        String sigName = sigAlgId.getName();
         if (sigProvider.isEmpty()) {
-            sigVerf = Signature.getInstance(sigAlgId.getName());
+            sigVerf = Signature.getInstance(sigName);
         } else {
-            sigVerf = Signature.getInstance(sigAlgId.getName(), sigProvider);
+            sigVerf = Signature.getInstance(sigName, sigProvider);
         }
 
-        sigVerf.initVerify(key);
-
-        // set parameters after Signature.initSign/initVerify call,
-        // so the deferred provider selection happens when key is set
         try {
-            SignatureUtil.specialSetParameter(sigVerf, getSigAlgParams());
+            SignatureUtil.initVerifyWithParam(sigVerf, key,
+                SignatureUtil.getParamSpec(sigName, getSigAlgParams()));
         } catch (ProviderException e) {
             throw new CRLException(e.getMessage(), e.getCause());
         } catch (InvalidAlgorithmParameterException e) {
@@ -425,18 +423,16 @@
             throw new CRLException("Uninitialized CRL");
         }
         Signature sigVerf = null;
+        String sigName = sigAlgId.getName();
         if (sigProvider == null) {
-            sigVerf = Signature.getInstance(sigAlgId.getName());
+            sigVerf = Signature.getInstance(sigName);
         } else {
-            sigVerf = Signature.getInstance(sigAlgId.getName(), sigProvider);
+            sigVerf = Signature.getInstance(sigName, sigProvider);
         }
 
-        sigVerf.initVerify(key);
-
-        // set parameters after Signature.initSign/initVerify call,
-        // so the deferred provider selection happens when key is set
         try {
-            SignatureUtil.specialSetParameter(sigVerf, getSigAlgParams());
+            SignatureUtil.initVerifyWithParam(sigVerf, key,
+                SignatureUtil.getParamSpec(sigName, getSigAlgParams()));
         } catch (ProviderException e) {
             throw new CRLException(e.getMessage(), e.getCause());
         } catch (InvalidAlgorithmParameterException e) {
@@ -502,7 +498,7 @@
 
             sigEngine.initSign(key);
 
-                                // in case the name is reset
+            // in case the name is reset
             sigAlgId = AlgorithmId.get(sigEngine.getAlgorithm());
             infoSigAlgId = sigAlgId;
 
--- a/src/java.base/share/classes/sun/security/x509/X509CertImpl.java	Tue Apr 09 16:32:22 2019 -0700
+++ b/src/java.base/share/classes/sun/security/x509/X509CertImpl.java	Wed Apr 10 02:35:18 2019 +0000
@@ -422,18 +422,16 @@
         }
         // Verify the signature ...
         Signature sigVerf = null;
+        String sigName = algId.getName();
         if (sigProvider.isEmpty()) {
-            sigVerf = Signature.getInstance(algId.getName());
+            sigVerf = Signature.getInstance(sigName);
         } else {
-            sigVerf = Signature.getInstance(algId.getName(), sigProvider);
+            sigVerf = Signature.getInstance(sigName, sigProvider);
         }
 
-        sigVerf.initVerify(key);
-
-        // set parameters after Signature.initSign/initVerify call,
-        // so the deferred provider selection happens when key is set
         try {
-            SignatureUtil.specialSetParameter(sigVerf, getSigAlgParams());
+            SignatureUtil.initVerifyWithParam(sigVerf, key,
+                SignatureUtil.getParamSpec(sigName, getSigAlgParams()));
         } catch (ProviderException e) {
             throw new CertificateException(e.getMessage(), e.getCause());
         } catch (InvalidAlgorithmParameterException e) {
@@ -478,18 +476,16 @@
         }
         // Verify the signature ...
         Signature sigVerf = null;
+        String sigName = algId.getName();
         if (sigProvider == null) {
-            sigVerf = Signature.getInstance(algId.getName());
+            sigVerf = Signature.getInstance(sigName);
         } else {
-            sigVerf = Signature.getInstance(algId.getName(), sigProvider);
+            sigVerf = Signature.getInstance(sigName, sigProvider);
         }
 
-        sigVerf.initVerify(key);
-
-        // set parameters after Signature.initSign/initVerify call,
-        // so the deferred provider selection happens when key is set
         try {
-            SignatureUtil.specialSetParameter(sigVerf, getSigAlgParams());
+            SignatureUtil.initVerifyWithParam(sigVerf, key,
+                SignatureUtil.getParamSpec(sigName, getSigAlgParams()));
         } catch (ProviderException e) {
             throw new CertificateException(e.getMessage(), e.getCause());
         } catch (InvalidAlgorithmParameterException e) {
@@ -587,22 +583,19 @@
             InvalidKeyException, InvalidAlgorithmParameterException,
             NoSuchProviderException, SignatureException {
         try {
-            if (readOnly)
+            if (readOnly) {
                 throw new CertificateEncodingException(
-                              "cannot over-write existing certificate");
+                        "cannot over-write existing certificate");
+            }
             Signature sigEngine = null;
-            if (provider == null || provider.isEmpty())
+            if (provider == null || provider.isEmpty()) {
                 sigEngine = Signature.getInstance(algorithm);
-            else
+            } else {
                 sigEngine = Signature.getInstance(algorithm, provider);
+            }
 
-            sigEngine.initSign(key);
-
-            if (signingParams != null) {
-                // set parameters after Signature.initSign/initVerify call, so
-                // the deferred provider selection happens when the key is set
-                sigEngine.setParameter(signingParams);
-            }
+            SignatureUtil.initSignWithParam(sigEngine, key, signingParams,
+                    null);
 
             // in case the name is reset
             if (signingParams != null) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/security/Signature/SignatureGetInstance.java	Wed Apr 10 02:35:18 2019 +0000
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8216039
+ * @summary Ensure the BC provider-reselection workaround in Signature class
+ *     functions correctly
+ * @modules java.base/sun.security.util
+ * @run main/othervm SignatureGetInstance
+ */
+import java.security.*;
+import java.security.interfaces.*;
+import java.security.spec.*;
+import sun.security.util.SignatureUtil;
+
+public class SignatureGetInstance {
+
+    private static final String SIGALG = "RSASSA-PSS";
+
+    public static void main(String[] args) throws Exception {
+        Provider testProvider = new TestProvider();
+        // put test provider before SunRsaSign provider
+        Security.insertProviderAt(testProvider, 1);
+        //Security.addProvider(testProvider);
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+        KeyPair kp = kpg.generateKeyPair();
+
+        MyPrivKey testPriv = new MyPrivKey();
+        MyPubKey testPub = new MyPubKey();
+
+        testDblInit(testPriv, testPub, true, "TestProvider");
+        testDblInit(kp.getPrivate(), kp.getPublic(), true, "SunRsaSign");
+        testDblInit(testPriv, kp.getPublic(), false, null);
+        testDblInit(kp.getPrivate(), testPub, false, null);
+
+        testSetAndInit(null, testPriv, true);
+        testSetAndInit(null, testPub, true);
+        testSetAndInit(null, kp.getPrivate(), true);
+        testSetAndInit(null, kp.getPublic(), true);
+
+        String provName = "SunRsaSign";
+        testSetAndInit(provName, testPriv, false);
+        testSetAndInit(provName, testPub, false);
+        testSetAndInit(provName, kp.getPrivate(), true);
+        testSetAndInit(provName, kp.getPublic(), true);
+
+        provName = "TestProvider";
+        testSetAndInit(provName, testPriv, true);
+        testSetAndInit(provName, testPub, true);
+        testSetAndInit(provName, kp.getPrivate(), false);
+        testSetAndInit(provName, kp.getPublic(), false);
+
+        System.out.println("Test Passed");
+    }
+
+    private static void checkName(Signature s, String name) {
+        if (name != null &&
+            !(name.equals(s.getProvider().getName()))) {
+            throw new RuntimeException("Fail: provider name mismatch");
+        }
+    }
+
+    private static void testDblInit(PrivateKey key1, PublicKey key2,
+            boolean shouldPass, String expectedProvName) throws Exception {
+        Signature sig = Signature.getInstance(SIGALG);
+        SignatureUtil.initSignWithParam(sig, key1, PSSParameterSpec.DEFAULT, null);
+        try {
+            sig.initVerify(key2);
+            if (!shouldPass) {
+                throw new RuntimeException("Fail: should throw InvalidKeyException");
+            }
+            checkName(sig, expectedProvName);
+        } catch (InvalidKeyException ike) {
+            if (shouldPass) {
+                System.out.println("Fail: Unexpected InvalidKeyException");
+                throw ike;
+            }
+        }
+    }
+
+    private static void testSetAndInit(String provName, Key key,
+            boolean shouldPass) throws Exception {
+        Signature sig;
+        if (provName == null) {
+            sig = Signature.getInstance(SIGALG);
+        } else {
+            sig = Signature.getInstance(SIGALG, provName);
+        }
+        AlgorithmParameterSpec params = PSSParameterSpec.DEFAULT;
+        boolean doSign = (key instanceof PrivateKey);
+        try {
+            if (doSign) {
+                SignatureUtil.initSignWithParam(sig, (PrivateKey)key, params, null);
+            } else {
+                SignatureUtil.initVerifyWithParam(sig, (PublicKey)key, params);
+            }
+            if (!shouldPass) {
+                throw new RuntimeException("Fail: should throw InvalidKeyException");
+            }
+            checkName(sig, provName);
+            // check that the earlier parameter is still there
+            if (sig.getParameters() == null) {
+                throw new RuntimeException("Fail: parameters not preserved");
+            }
+        } catch (InvalidKeyException ike) {
+            if (shouldPass) {
+                System.out.println("Fail: Unexpected InvalidKeyException");
+                throw ike;
+            }
+        }
+    }
+
+    // Test provider which only accepts its own Key objects
+    // Registered to be more preferred than SunRsaSign provider
+    // for testing deferred provider selection
+    public static class TestProvider extends Provider {
+        TestProvider() {
+            super("TestProvider", "1.0", "provider for SignatureGetInstance");
+            put("Signature.RSASSA-PSS",
+                "SignatureGetInstance$MySigImpl");
+        }
+    }
+
+    public static class MyPrivKey implements PrivateKey {
+        public String getAlgorithm() { return "RSASSA-PSS"; }
+        public String getFormat() { return "MyOwn"; }
+        public byte[] getEncoded() { return null; }
+    }
+
+    public static class MyPubKey implements PublicKey {
+        public String getAlgorithm() { return "RSASSA-PSS"; }
+        public String getFormat() { return "MyOwn"; }
+        public byte[] getEncoded() { return null; }
+    }
+
+    public static class MySigImpl extends SignatureSpi {
+        // simulate BC behavior of only using params set before init calls
+        AlgorithmParameterSpec initParamSpec = null;
+        AlgorithmParameterSpec paramSpec = null;
+
+        public MySigImpl() {
+            super();
+        }
+
+        @Override
+        protected void engineInitVerify(PublicKey publicKey)
+                throws InvalidKeyException {
+            if (!(publicKey instanceof MyPubKey)) {
+                throw new InvalidKeyException("Must be MyPubKey");
+            }
+            initParamSpec = paramSpec;
+        }
+
+        @Override
+        protected void engineInitSign(PrivateKey privateKey)
+                throws InvalidKeyException {
+            if (!(privateKey instanceof MyPrivKey)) {
+                throw new InvalidKeyException("Must be MyPrivKey");
+            }
+            initParamSpec = paramSpec;
+        }
+
+        @Override
+        protected void engineUpdate(byte b) throws SignatureException {
+        }
+
+        @Override
+        protected void engineUpdate(byte[] b, int off, int len)
+                throws SignatureException {
+        }
+
+        @Override
+        protected byte[] engineSign()
+                throws SignatureException {
+            return new byte[0];
+        }
+
+        @Override
+        protected boolean engineVerify(byte[] sigBytes)
+                throws SignatureException {
+            return false;
+        }
+
+        @Override
+        @Deprecated
+        protected void engineSetParameter(String param, Object value)
+                throws InvalidParameterException {
+        }
+
+        @Override
+        protected void engineSetParameter(AlgorithmParameterSpec params)
+                throws InvalidAlgorithmParameterException {
+            paramSpec = params;
+        }
+
+        @Override
+        @Deprecated
+        protected AlgorithmParameters engineGetParameter(String param)
+                throws InvalidParameterException {
+            return null;
+        }
+
+        @Override
+        protected AlgorithmParameters engineGetParameters() {
+            if (initParamSpec != null) {
+                try {
+                    AlgorithmParameters ap =
+                        AlgorithmParameters.getInstance("RSASSA-PSS");
+                    ap.init(initParamSpec);
+                    return ap;
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+            return null;
+        }
+    }
+}
--- a/test/jdk/sun/security/util/misc/SetNullSigParams.java	Tue Apr 09 16:32:22 2019 -0700
+++ b/test/jdk/sun/security/util/misc/SetNullSigParams.java	Wed Apr 10 02:35:18 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,8 +23,8 @@
 
 /*
  * @test
- * @bug 8214096
- * @summary Make sure SignatureUtil can accept null algorithm parameters
+ * @bug 8214096 8216039
+ * @summary Make sure SignatureUtil works with null algorithm parameters
  * @modules java.base/sun.security.util
  */
 import java.security.*;
@@ -35,8 +35,8 @@
 
     public static void main(String[] args) throws Exception {
         Signature sig = new SpecialSigImpl();
-        SignatureUtil.specialSetParameter(sig, (byte[]) null);
-        SignatureUtil.specialSetParameter(sig, (AlgorithmParameters) null);
+        SignatureUtil.initVerifyWithParam(sig, (PublicKey) null, null);
+        SignatureUtil.initSignWithParam(sig, null, null, null);
     }
 
     // Sample Signature impl class which simulates 3rd party provider behavior