8223063: Support CNG RSA keys
authorweijun
Tue, 14 May 2019 08:47:13 +0800
changeset 54827 01fa7f06f806
parent 54826 4ef4d55a129b
child 54828 a7abac394abb
8223063: Support CNG RSA keys Reviewed-by: mullan
src/java.base/share/classes/sun/security/tools/keytool/Main.java
src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi/CPrivateKey.java
src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi/CPublicKey.java
src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi/CSignature.java
src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp
test/jdk/sun/security/mscapi/VeryLongAlias.java
--- a/src/java.base/share/classes/sun/security/tools/keytool/Main.java	Mon May 13 13:32:38 2019 -0700
+++ b/src/java.base/share/classes/sun/security/tools/keytool/Main.java	Tue May 14 08:47:13 2019 +0800
@@ -3578,6 +3578,11 @@
     {
         Key key = null;
 
+        if (KeyStoreUtil.isWindowsKeyStore(storetype)) {
+            key = keyStore.getKey(alias, null);
+            return Pair.of(key, null);
+        }
+
         if (keyStore.containsAlias(alias) == false) {
             MessageFormat form = new MessageFormat
                 (rb.getString("Alias.alias.does.not.exist"));
--- a/src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi/CPrivateKey.java	Mon May 13 13:32:38 2019 -0700
+++ b/src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi/CPrivateKey.java	Tue May 14 08:47:13 2019 +0800
@@ -62,9 +62,13 @@
     }
 
     public String toString() {
-        return algorithm + "PrivateKey [size=" + keyLength + " bits, type=" +
-            getKeyType(handles.hCryptKey) + ", container=" +
-            getContainerName(handles.hCryptProv) + "]";
+        if (handles.hCryptKey != 0) {
+            return algorithm + "PrivateKey [size=" + keyLength + " bits, type=" +
+                    getKeyType(handles.hCryptKey) + ", container=" +
+                    getContainerName(handles.hCryptProv) + "]";
+        } else {
+            return algorithm + "PrivateKey [size=" + keyLength + " bits, type=CNG]";
+        }
     }
 
     // This class is not serializable
--- a/src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi/CPublicKey.java	Mon May 13 13:32:38 2019 -0700
+++ b/src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi/CPublicKey.java	Tue May 14 08:47:13 2019 +0800
@@ -128,9 +128,14 @@
         public String toString() {
             StringBuffer sb = new StringBuffer();
             sb.append(algorithm + "PublicKey [size=").append(keyLength)
-                    .append(" bits, type=").append(getKeyType(handles.hCryptKey))
-                    .append(", container=").append(getContainerName(handles.hCryptProv))
-                    .append("]\n  modulus: ").append(getModulus())
+                    .append(" bits, type=");
+            if (handles.hCryptKey != 0) {
+                sb.append(getKeyType(handles.hCryptKey))
+                        .append(", container=").append(getContainerName(handles.hCryptProv));
+            } else {
+                sb.append("CNG");
+            }
+            sb.append("]\n  modulus: ").append(getModulus())
                     .append("\n  public exponent: ").append(getPublicExponent());
             return sb.toString();
         }
--- a/src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi/CSignature.java	Mon May 13 13:32:38 2019 -0700
+++ b/src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi/CSignature.java	Tue May 14 08:47:13 2019 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 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
@@ -121,7 +121,8 @@
 
             if ((key instanceof CPrivateKey) == false
                     || !key.getAlgorithm().equalsIgnoreCase("RSA")) {
-                throw new InvalidKeyException("Key type not supported");
+                throw new InvalidKeyException("Key type not supported: "
+                        + key.getClass() + " " + key.getAlgorithm());
             }
             privateKey = (CPrivateKey) key;
 
@@ -201,16 +202,22 @@
 
             byte[] hash = getDigestValue();
 
-            // Omit the hash OID when generating a NONEwithRSA signature
-            boolean noHashOID = this instanceof NONEwithRSA;
-
-            // Sign hash using MS Crypto APIs
-            byte[] result = signHash(noHashOID, hash, hash.length,
+            if (privateKey.getHCryptKey() == 0) {
+                return signCngHash(1, hash, hash.length,
+                        0,
+                        this instanceof NONEwithRSA ? null : messageDigestAlgorithm,
+                        privateKey.getHCryptProvider(), 0);
+            } else {
+                // Omit the hash OID when generating a NONEwithRSA signature
+                boolean noHashOID = this instanceof NONEwithRSA;
+                // Sign hash using MS Crypto APIs
+                byte[] result = signHash(noHashOID, hash, hash.length,
                         messageDigestAlgorithm, privateKey.getHCryptProvider(),
                         privateKey.getHCryptKey());
 
-            // Convert signature array from little endian to big endian
-            return convertEndianArray(result);
+                // Convert signature array from little endian to big endian
+                return convertEndianArray(result);
+            }
         }
 
         /**
@@ -230,10 +237,20 @@
                 throws SignatureException {
             byte[] hash = getDigestValue();
 
-            return verifySignedHash(hash, hash.length,
-                    messageDigestAlgorithm, convertEndianArray(sigBytes),
-                    sigBytes.length, publicKey.getHCryptProvider(),
-                    publicKey.getHCryptKey());
+            if (publicKey.getHCryptKey() == 0) {
+                return verifyCngSignedHash(
+                        1, hash, hash.length,
+                        sigBytes, sigBytes.length,
+                        0,
+                        messageDigestAlgorithm,
+                        publicKey.getHCryptProvider(),
+                        0);
+            } else {
+                return verifySignedHash(hash, hash.length,
+                        messageDigestAlgorithm, convertEndianArray(sigBytes),
+                        sigBytes.length, publicKey.getHCryptProvider(),
+                        publicKey.getHCryptKey());
+            }
         }
 
         /**
--- a/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp	Mon May 13 13:32:38 2019 -0700
+++ b/src/jdk.crypto.mscapi/windows/native/libsunmscapi/security.cpp	Tue May 14 08:47:13 2019 +0800
@@ -649,19 +649,28 @@
                                 if (::NCryptGetProperty(
                                         hCryptProv, NCRYPT_ALGORITHM_PROPERTY,
                                         (PBYTE)buffer, 32, &len, NCRYPT_SILENT_FLAG) == ERROR_SUCCESS) {
+                                    jstring name = env->NewStringUTF(pszNameString);
+                                    if (name == NULL) {
+                                        __leave;
+                                    }
                                     if (buffer[0] == 'E' && buffer[2] == 'C'
                                             && (dwPublicKeyLength == 256
                                                     || dwPublicKeyLength == 384
                                                     || dwPublicKeyLength == 521)) {
-                                        jstring name = env->NewStringUTF(pszNameString);
-                                        if (name == NULL) {
-                                            __leave;
-                                        }
                                         env->CallVoidMethod(obj, mGenKeyAndCertChain,
                                             0,
                                             name,
                                             (jlong) hCryptProv, 0,
                                             dwPublicKeyLength, jArrayList);
+                                    } else if (buffer[0] == 'R' && buffer[2] == 'S'
+                                            && buffer[4] == 'A') {
+                                        env->CallVoidMethod(obj, mGenKeyAndCertChain,
+                                            1,
+                                            name,
+                                            (jlong) hCryptProv, 0,
+                                            dwPublicKeyLength, jArrayList);
+                                    } else {
+                                        dump("Unknown NCRYPT_ALGORITHM_PROPERTY", buffer, len);
                                     }
                                 }
                             }
@@ -890,11 +899,15 @@
             break;
         case 1:
             BCRYPT_PKCS1_PADDING_INFO pkcs1Info;
-            pkcs1Info.pszAlgId = MapHashIdentifier(env, jHashAlgorithm);
-            if (pkcs1Info.pszAlgId == NULL) {
-                ThrowExceptionWithMessage(env, SIGNATURE_EXCEPTION,
-                        "Unrecognised hash algorithm");
-                __leave;
+            if (jHashAlgorithm) {
+                pkcs1Info.pszAlgId = MapHashIdentifier(env, jHashAlgorithm);
+                if (pkcs1Info.pszAlgId == NULL) {
+                    ThrowExceptionWithMessage(env, SIGNATURE_EXCEPTION,
+                            "Unrecognised hash algorithm");
+                    __leave;
+                }
+            } else {
+                pkcs1Info.pszAlgId = NULL;
             }
             param = &pkcs1Info;
             dwFlags = BCRYPT_PAD_PKCS1;
@@ -1121,11 +1134,15 @@
             break;
         case 1:
             BCRYPT_PKCS1_PADDING_INFO pkcs1Info;
-            pkcs1Info.pszAlgId = MapHashIdentifier(env, jHashAlgorithm);
-            if (pkcs1Info.pszAlgId == NULL) {
-                ThrowExceptionWithMessage(env, SIGNATURE_EXCEPTION,
-                        "Unrecognised hash algorithm");
-                __leave;
+            if (jHashAlgorithm) {
+                pkcs1Info.pszAlgId = MapHashIdentifier(env, jHashAlgorithm);
+                if (pkcs1Info.pszAlgId == NULL) {
+                    ThrowExceptionWithMessage(env, SIGNATURE_EXCEPTION,
+                            "Unrecognised hash algorithm");
+                    __leave;
+                }
+            } else {
+                pkcs1Info.pszAlgId = NULL;
             }
             param = &pkcs1Info;
             dwFlags = NCRYPT_PAD_PKCS1_FLAG;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/mscapi/VeryLongAlias.java	Tue May 14 08:47:13 2019 +0800
@@ -0,0 +1,116 @@
+/*
+ * 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 8223063
+ * @requires os.family == "windows"
+ * @library /test/lib
+ * @summary Support CNG RSA keys
+ */
+
+import jdk.test.lib.SecurityTools;
+import jdk.test.lib.process.ProcessTools;
+
+import java.io.File;
+import java.security.KeyStore;
+import java.security.MessageDigest;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.cert.X509Certificate;
+import java.util.List;
+import java.util.Random;
+
+public class VeryLongAlias {
+
+    static String alias = String.format("%0512d", new Random().nextInt(100000));
+
+    public static void main(String[] args) throws Throwable {
+
+        SecurityTools.keytool("-genkeypair -storetype pkcs12 -keystore ks"
+                + " -storepass changeit -keyalg RSA -dname CN=A -alias "
+                + alias);
+        String id = ((X509Certificate)KeyStore.getInstance(
+                    new File("ks"), "changeit".toCharArray())
+                .getCertificate(alias)).getSerialNumber().toString(16);
+        try {
+            // Importing pkcs12 file. Long alias is only supported by CNG.
+            ProcessTools.executeCommand("certutil", "-v", "-p", "changeit",
+                        "-csp", "Microsoft Software Key Storage Provider",
+                        "-user", "-importpfx", "MY", "ks", "NoRoot,NoExport")
+                    .shouldHaveExitValue(0);
+            test();
+        } finally {
+            ProcessTools.executeCommand("certutil", "-user", "-delstore", "MY",
+                    id);
+        }
+    }
+
+    static void test() throws Exception {
+
+        char[] pass = "changeit".toCharArray();
+
+        KeyStore k1 = KeyStore.getInstance("Windows-MY");
+        k1.load(null, null);
+
+        KeyStore k2 = KeyStore.getInstance(new File("ks"), pass);
+
+        PrivateKey p1 = (PrivateKey)k1.getKey(alias, null);
+        PublicKey u1 = k1.getCertificate(alias).getPublicKey();
+
+        PrivateKey p2 = (PrivateKey)k2.getKey(alias, pass);
+        PublicKey u2 = k2.getCertificate(alias).getPublicKey();
+
+        System.out.println(p1.toString());
+        System.out.println(u1.toString());
+        if (!p1.toString().contains("type=CNG")) {
+            throw new Exception("Not a CNG key");
+        }
+
+        testSignature(p1, u1);
+        testSignature(p1, u2);
+        testSignature(p2, u1);
+        testSignature(p2, u2);
+    }
+
+    static void testSignature(PrivateKey p, PublicKey u) throws Exception {
+        byte[] data = "hello".getBytes();
+        for (String alg : List.of(
+                "NONEwithRSA", "SHA1withRSA",
+                "SHA256withRSA", "SHA512withRSA")) {
+            if (alg.contains("NONE")) {
+                data = MessageDigest.getInstance("SHA-256").digest(data);
+            }
+            Signature s1 = Signature.getInstance(alg);
+            Signature s2 = Signature.getInstance(alg);
+            s1.initSign(p);
+            s2.initVerify(u);
+            s1.update(data);
+            s2.update(data);
+            if (!s2.verify(s1.sign())) {
+                throw new Exception("Error");
+            }
+        }
+    }
+}