diff --git a/jdk/src/windows/classes/sun/security/mscapi/RSASignature.java b/jdk/src/windows/classes/sun/security/mscapi/RSASignature.java index 7e80490e652..9a735d5fd89 100644 --- a/jdk/src/windows/classes/sun/security/mscapi/RSASignature.java +++ b/jdk/src/windows/classes/sun/security/mscapi/RSASignature.java @@ -50,6 +50,9 @@ import sun.security.rsa.RSAKeyFactory; * following algorithm names: * * . "SHA1withRSA" + * . "SHA256withRSA" + * . "SHA384withRSA" + * . "SHA512withRSA" * . "MD5withRSA" * . "MD2withRSA" * @@ -63,7 +66,10 @@ abstract class RSASignature extends java.security.SignatureSpi // message digest implementation we use private final MessageDigest messageDigest; - // flag indicating whether the digest is reset + // message digest name + private final String messageDigestAlgorithm; + + // flag indicating whether the digest has been reset private boolean needsReset; // the signing key @@ -73,10 +79,15 @@ abstract class RSASignature extends java.security.SignatureSpi private Key publicKey = null; + /** + * Constructs a new RSASignature. Used by subclasses. + */ RSASignature(String digestName) { try { messageDigest = MessageDigest.getInstance(digestName); + // Get the digest's canonical name + messageDigestAlgorithm = messageDigest.getAlgorithm(); } catch (NoSuchAlgorithmException e) { throw new ProviderException(e); @@ -91,6 +102,24 @@ abstract class RSASignature extends java.security.SignatureSpi } } + public static final class SHA256 extends RSASignature { + public SHA256() { + super("SHA-256"); + } + } + + public static final class SHA384 extends RSASignature { + public SHA384() { + super("SHA-384"); + } + } + + public static final class SHA512 extends RSASignature { + public SHA512() { + super("SHA-512"); + } + } + public static final class MD5 extends RSASignature { public MD5() { super("MD5"); @@ -103,16 +132,7 @@ abstract class RSASignature extends java.security.SignatureSpi } } - /** - * 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. - * - * @exception InvalidKeyException if the key is improperly - * encoded, parameters are missing, and so on. - */ + // initialize for signing. See JCA doc protected void engineInitVerify(PublicKey key) throws InvalidKeyException { @@ -158,24 +178,12 @@ abstract class RSASignature extends java.security.SignatureSpi publicKey = (sun.security.mscapi.RSAPublicKey) key; } - if (needsReset) { - messageDigest.reset(); - needsReset = false; - } + this.privateKey = null; + resetDigest(); } - /** - * Initializes this signature object with the specified - * private key for signing operations. - * - * @param privateKey the private key of the identity whose signature - * will be generated. - * - * @exception InvalidKeyException if the key is improperly - * encoded, parameters are missing, and so on. - */ - protected void engineInitSign(PrivateKey key) - throws InvalidKeyException + // initialize for signing. See JCA doc + protected void engineInitSign(PrivateKey key) throws InvalidKeyException { // This signature accepts only RSAPrivateKey if ((key instanceof sun.security.mscapi.RSAPrivateKey) == false) { @@ -189,12 +197,25 @@ abstract class RSASignature extends java.security.SignatureSpi null, RSAKeyPairGenerator.KEY_SIZE_MIN, RSAKeyPairGenerator.KEY_SIZE_MAX); + this.publicKey = null; + resetDigest(); + } + + /** + * Resets the message digest if needed. + */ + private void resetDigest() { if (needsReset) { messageDigest.reset(); needsReset = false; } } + private byte[] getDigestValue() { + needsReset = false; + return messageDigest.digest(); + } + /** * Updates the data to be signed or verified * using the specified byte. @@ -254,13 +275,12 @@ abstract class RSASignature extends java.security.SignatureSpi */ protected byte[] engineSign() throws SignatureException { - byte[] hash = messageDigest.digest(); - needsReset = false; + byte[] hash = getDigestValue(); // Sign hash using MS Crypto APIs byte[] result = signHash(hash, hash.length, - messageDigest.getAlgorithm(), privateKey.getHCryptProvider(), + messageDigestAlgorithm, privateKey.getHCryptProvider(), privateKey.getHCryptKey()); // Convert signature array from little endian to big endian @@ -314,11 +334,10 @@ abstract class RSASignature extends java.security.SignatureSpi protected boolean engineVerify(byte[] sigBytes) throws SignatureException { - byte[] hash = messageDigest.digest(); - needsReset = false; + byte[] hash = getDigestValue(); return verifySignedHash(hash, hash.length, - messageDigest.getAlgorithm(), convertEndianArray(sigBytes), + messageDigestAlgorithm, convertEndianArray(sigBytes), sigBytes.length, publicKey.getHCryptProvider(), publicKey.getHCryptKey()); } diff --git a/jdk/src/windows/classes/sun/security/mscapi/SunMSCAPI.java b/jdk/src/windows/classes/sun/security/mscapi/SunMSCAPI.java index 55feb7e1d84..b101e5b09a5 100644 --- a/jdk/src/windows/classes/sun/security/mscapi/SunMSCAPI.java +++ b/jdk/src/windows/classes/sun/security/mscapi/SunMSCAPI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2011, 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 @@ -81,6 +81,12 @@ public final class SunMSCAPI extends Provider { */ map.put("Signature.SHA1withRSA", "sun.security.mscapi.RSASignature$SHA1"); + map.put("Signature.SHA256withRSA", + "sun.security.mscapi.RSASignature$SHA256"); + map.put("Signature.SHA384withRSA", + "sun.security.mscapi.RSASignature$SHA384"); + map.put("Signature.SHA512withRSA", + "sun.security.mscapi.RSASignature$SHA512"); map.put("Signature.MD5withRSA", "sun.security.mscapi.RSASignature$MD5"); map.put("Signature.MD2withRSA", @@ -89,12 +95,16 @@ public final class SunMSCAPI extends Provider { // supported key classes map.put("Signature.SHA1withRSA SupportedKeyClasses", "sun.security.mscapi.Key"); + map.put("Signature.SHA256withRSA SupportedKeyClasses", + "sun.security.mscapi.Key"); + map.put("Signature.SHA384withRSA SupportedKeyClasses", + "sun.security.mscapi.Key"); + map.put("Signature.SHA512withRSA SupportedKeyClasses", + "sun.security.mscapi.Key"); map.put("Signature.MD5withRSA SupportedKeyClasses", "sun.security.mscapi.Key"); map.put("Signature.MD2withRSA SupportedKeyClasses", "sun.security.mscapi.Key"); - map.put("Signature.NONEwithRSA SupportedKeyClasses", - "sun.security.mscapi.Key"); /* * Key Pair Generator engines diff --git a/jdk/src/windows/native/sun/security/mscapi/security.cpp b/jdk/src/windows/native/sun/security/mscapi/security.cpp index f92cc9a9e9e..3b0421576c9 100644 --- a/jdk/src/windows/native/sun/security/mscapi/security.cpp +++ b/jdk/src/windows/native/sun/security/mscapi/security.cpp @@ -483,6 +483,7 @@ JNIEXPORT jbyteArray JNICALL Java_sun_security_mscapi_RSASignature_signHash jbyte* pHashBuffer = NULL; jbyte* pSignedHashBuffer = NULL; jbyteArray jSignedHash = NULL; + HCRYPTPROV hCryptProvAlt = NULL; __try { @@ -492,8 +493,32 @@ JNIEXPORT jbyteArray JNICALL Java_sun_security_mscapi_RSASignature_signHash // Acquire a hash object handle. if (::CryptCreateHash(HCRYPTPROV(hCryptProv), algId, 0, 0, &hHash) == FALSE) { - ThrowException(env, SIGNATURE_EXCEPTION, GetLastError()); - __leave; + // Failover to using the PROV_RSA_AES CSP + + DWORD cbData = 256; + BYTE pbData[256]; + pbData[0] = '\0'; + + // Get name of the key container + ::CryptGetProvParam((HCRYPTPROV)hCryptProv, PP_CONTAINER, + (BYTE *)pbData, &cbData, 0); + + // Acquire an alternative CSP handle + if (::CryptAcquireContext(&hCryptProvAlt, LPCSTR(pbData), NULL, + PROV_RSA_AES, 0) == FALSE) + { + + ThrowException(env, SIGNATURE_EXCEPTION, GetLastError()); + __leave; + } + + // Acquire a hash object handle. + if (::CryptCreateHash(HCRYPTPROV(hCryptProvAlt), algId, 0, 0, + &hHash) == FALSE) + { + ThrowException(env, SIGNATURE_EXCEPTION, GetLastError()); + __leave; + } } // Copy hash from Java to native buffer @@ -546,6 +571,9 @@ JNIEXPORT jbyteArray JNICALL Java_sun_security_mscapi_RSASignature_signHash } __finally { + if (hCryptProvAlt) + ::CryptReleaseContext(hCryptProvAlt, 0); + if (pSignedHashBuffer) delete [] pSignedHashBuffer; @@ -574,6 +602,7 @@ JNIEXPORT jboolean JNICALL Java_sun_security_mscapi_RSASignature_verifySignedHas jbyte* pSignedHashBuffer = NULL; DWORD dwSignedHashBufferLen = jSignedHashSize; jboolean result = JNI_FALSE; + HCRYPTPROV hCryptProvAlt = NULL; __try { @@ -584,8 +613,32 @@ JNIEXPORT jboolean JNICALL Java_sun_security_mscapi_RSASignature_verifySignedHas if (::CryptCreateHash(HCRYPTPROV(hCryptProv), algId, 0, 0, &hHash) == FALSE) { - ThrowException(env, SIGNATURE_EXCEPTION, GetLastError()); - __leave; + // Failover to using the PROV_RSA_AES CSP + + DWORD cbData = 256; + BYTE pbData[256]; + pbData[0] = '\0'; + + // Get name of the key container + ::CryptGetProvParam((HCRYPTPROV)hCryptProv, PP_CONTAINER, + (BYTE *)pbData, &cbData, 0); + + // Acquire an alternative CSP handle + if (::CryptAcquireContext(&hCryptProvAlt, LPCSTR(pbData), NULL, + PROV_RSA_AES, 0) == FALSE) + { + + ThrowException(env, SIGNATURE_EXCEPTION, GetLastError()); + __leave; + } + + // Acquire a hash object handle. + if (::CryptCreateHash(HCRYPTPROV(hCryptProvAlt), algId, 0, 0, + &hHash) == FALSE) + { + ThrowException(env, SIGNATURE_EXCEPTION, GetLastError()); + __leave; + } } // Copy hash and signedHash from Java to native buffer @@ -616,6 +669,9 @@ JNIEXPORT jboolean JNICALL Java_sun_security_mscapi_RSASignature_verifySignedHas __finally { + if (hCryptProvAlt) + ::CryptReleaseContext(hCryptProvAlt, 0); + if (pSignedHashBuffer) delete [] pSignedHashBuffer; @@ -648,15 +704,27 @@ JNIEXPORT jobject JNICALL Java_sun_security_mscapi_RSAKeyPairGenerator_generateR pszKeyContainerName = env->GetStringUTFChars(keyContainerName, NULL); // Acquire a CSP context (create a new key container). + // Prefer a PROV_RSA_AES CSP, when available, due to its support + // for SHA-2-based signatures. if (::CryptAcquireContext( &hCryptProv, pszKeyContainerName, NULL, - PROV_RSA_FULL, + PROV_RSA_AES, CRYPT_NEWKEYSET) == FALSE) { - ThrowException(env, KEY_EXCEPTION, GetLastError()); - __leave; + // Failover to using the default CSP (PROV_RSA_FULL) + + if (::CryptAcquireContext( + &hCryptProv, + pszKeyContainerName, + NULL, + PROV_RSA_FULL, + CRYPT_NEWKEYSET) == FALSE) + { + ThrowException(env, KEY_EXCEPTION, GetLastError()); + __leave; + } } // Generate an RSA keypair @@ -1849,15 +1917,27 @@ JNIEXPORT jobject JNICALL Java_sun_security_mscapi_RSASignature_importPublicKey pbKeyBlob = (BYTE *) env->GetByteArrayElements(keyBlob, 0); // Acquire a CSP context (create a new key container). + // Prefer a PROV_RSA_AES CSP, when available, due to its support + // for SHA-2-based signatures. if (::CryptAcquireContext( &hCryptProv, NULL, NULL, - PROV_RSA_FULL, + PROV_RSA_AES, CRYPT_VERIFYCONTEXT) == FALSE) { - ThrowException(env, KEYSTORE_EXCEPTION, GetLastError()); - __leave; + // Failover to using the default CSP (PROV_RSA_FULL) + + if (::CryptAcquireContext( + &hCryptProv, + NULL, + NULL, + PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT) == FALSE) + { + ThrowException(env, KEYSTORE_EXCEPTION, GetLastError()); + __leave; + } } // Import the public key diff --git a/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.java b/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.java new file mode 100644 index 00000000000..e2bd06b1905 --- /dev/null +++ b/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2011, 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. + */ + +/** + * @see SignUsingSHA2withRSA.sh + */ + +import java.security.*; +import java.util.*; + +public class SignUsingSHA2withRSA { + + private static final byte[] toBeSigned = new byte[] { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10 + }; + + private static List generatedSignatures = new ArrayList<>(); + + public static void main(String[] args) throws Exception { + + Provider[] providers = Security.getProviders("Signature.SHA256withRSA"); + if (providers == null) { + System.out.println("No JCE providers support the " + + "'Signature.SHA256withRSA' algorithm"); + System.out.println("Skipping this test..."); + return; + + } else { + System.out.println("The following JCE providers support the " + + "'Signature.SHA256withRSA' algorithm: "); + for (Provider provider : providers) { + System.out.println(" " + provider.getName()); + } + } + System.out.println("-------------------------------------------------"); + + KeyStore ks = KeyStore.getInstance("Windows-MY", "SunMSCAPI"); + ks.load(null, null); + System.out.println("Loaded keystore: Windows-MY"); + + Enumeration e = ks.aliases(); + PrivateKey privateKey = null; + PublicKey publicKey = null; + + while (e.hasMoreElements()) { + String alias = (String) e.nextElement(); + if (alias.equals("6753664")) { + System.out.println("Loaded entry: " + alias); + privateKey = (PrivateKey) ks.getKey(alias, null); + publicKey = (PublicKey) ks.getCertificate(alias).getPublicKey(); + } + } + if (privateKey == null || publicKey == null) { + throw new Exception("Cannot load the keys need to run this test"); + } + System.out.println("-------------------------------------------------"); + + generatedSignatures.add(signUsing("SHA256withRSA", privateKey)); + generatedSignatures.add(signUsing("SHA384withRSA", privateKey)); + generatedSignatures.add(signUsing("SHA512withRSA", privateKey)); + + System.out.println("-------------------------------------------------"); + + verifyUsing("SHA256withRSA", publicKey, generatedSignatures.get(0)); + verifyUsing("SHA384withRSA", publicKey, generatedSignatures.get(1)); + verifyUsing("SHA512withRSA", publicKey, generatedSignatures.get(2)); + + System.out.println("-------------------------------------------------"); + } + + private static byte[] signUsing(String signAlgorithm, + PrivateKey privateKey) throws Exception { + + // Must explicitly specify the SunMSCAPI JCE provider + // (otherwise SunJCE is chosen because it appears earlier in the list) + Signature sig1 = Signature.getInstance(signAlgorithm, "SunMSCAPI"); + if (sig1 == null) { + throw new Exception("'" + signAlgorithm + "' is not supported"); + } + System.out.println("Using " + signAlgorithm + " signer from the " + + sig1.getProvider().getName() + " JCE provider"); + + System.out.println("Using key: " + privateKey); + sig1.initSign(privateKey); + sig1.update(toBeSigned); + byte [] sigBytes = null; + + try { + sigBytes = sig1.sign(); + System.out.println("Generated RSA signature over a " + + toBeSigned.length + "-byte data (signature length: " + + sigBytes.length * 8 + " bits)"); + System.out.println(String.format("0x%0" + + (sigBytes.length * 2) + "x", + new java.math.BigInteger(1, sigBytes))); + + } catch (SignatureException se) { + System.out.println("Error generating RSA signature: " + se); + } + + return sigBytes; + } + + private static void verifyUsing(String signAlgorithm, PublicKey publicKey, + byte[] signature) throws Exception { + + // Must explicitly specify the SunMSCAPI JCE provider + // (otherwise SunJCE is chosen because it appears earlier in the list) + Signature sig1 = Signature.getInstance(signAlgorithm, "SunMSCAPI"); + if (sig1 == null) { + throw new Exception("'" + signAlgorithm + "' is not supported"); + } + System.out.println("Using " + signAlgorithm + " verifier from the " + + sig1.getProvider().getName() + " JCE provider"); + + System.out.println("Using key: " + publicKey); + + System.out.println("\nVerifying RSA Signature over a " + + toBeSigned.length + "-byte data (signature length: " + + signature.length * 8 + " bits)"); + System.out.println(String.format("0x%0" + (signature.length * 2) + + "x", new java.math.BigInteger(1, signature))); + + sig1.initVerify(publicKey); + sig1.update(toBeSigned); + + if (sig1.verify(signature)) { + System.out.println("Verify PASSED\n"); + } else { + throw new Exception("Verify FAILED"); + } + } +} diff --git a/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.sh b/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.sh new file mode 100644 index 00000000000..c939fb39e3f --- /dev/null +++ b/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.sh @@ -0,0 +1,83 @@ +#!/bin/sh + +# +# Copyright (c) 2011, 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 6753664 +# @run shell SignUsingSHA2withRSA.sh +# @summary Support SHA256 (and higher) in SunMSCAPI + +# set a few environment variables so that the shell-script can run stand-alone +# in the source directory +if [ "${TESTSRC}" = "" ] ; then + TESTSRC="." +fi + +if [ "${TESTCLASSES}" = "" ] ; then + TESTCLASSES="." +fi + +if [ "${TESTJAVA}" = "" ] ; then + echo "TESTJAVA not set. Test cannot execute." + echo "FAILED!!!" + exit 1 +fi + +OS=`uname -s` +case "$OS" in + Windows* | CYGWIN* ) + + echo "Creating a temporary RSA keypair in the Windows-My store..." + ${TESTJAVA}/bin/keytool \ + -genkeypair \ + -storetype Windows-My \ + -keyalg RSA \ + -alias 6753664 \ + -dname "cn=6753664,c=US" \ + -noprompt + + echo + echo "Running the test..." + ${TESTJAVA}/bin/javac -d . ${TESTSRC}\\SignUsingSHA2withRSA.java + ${TESTJAVA}/bin/java SignUsingSHA2withRSA + + rc=$? + + echo + echo "Removing the temporary RSA keypair from the Windows-My store..." + ${TESTJAVA}/bin/keytool \ + -delete \ + -storetype Windows-My \ + -alias 6753664 + + echo done. + exit $rc + ;; + + * ) + echo "This test is not intended for '$OS' - passing test" + exit 0 + ;; +esac