--- a/jdk/src/windows/classes/sun/security/mscapi/RSASignature.java Thu Apr 28 10:14:12 2011 -0700
+++ b/jdk/src/windows/classes/sun/security/mscapi/RSASignature.java Fri Apr 29 00:21:54 2011 +0100
@@ -49,6 +49,7 @@
* Objects should be instantiated by calling Signature.getInstance() using the
* following algorithm names:
*
+ * . "NONEwithRSA"
* . "SHA1withRSA"
* . "SHA256withRSA"
* . "SHA384withRSA"
@@ -56,7 +57,12 @@
* . "MD5withRSA"
* . "MD2withRSA"
*
- * Note: RSA keys must be at least 512 bits long
+ * NOTE: RSA keys must be at least 512 bits long.
+ *
+ * NOTE: NONEwithRSA must be supplied with a pre-computed message digest.
+ * Only the following digest algorithms are supported: MD5, SHA-1,
+ * SHA-256, SHA-384, SHA-512 and a special-purpose digest algorithm
+ * which is a concatenation of SHA-1 and MD5 digests.
*
* @since 1.6
* @author Stanley Man-Kit Ho
@@ -67,7 +73,7 @@
private final MessageDigest messageDigest;
// message digest name
- private final String messageDigestAlgorithm;
+ private String messageDigestAlgorithm;
// flag indicating whether the digest has been reset
private boolean needsReset;
@@ -78,6 +84,13 @@
// the verification key
private Key publicKey = null;
+ /**
+ * Constructs a new RSASignature. Used by Raw subclass.
+ */
+ RSASignature() {
+ messageDigest = null;
+ messageDigestAlgorithm = null;
+ }
/**
* Constructs a new RSASignature. Used by subclasses.
@@ -96,6 +109,94 @@
needsReset = false;
}
+ // Nested class for NONEwithRSA signatures
+ public static final class Raw extends RSASignature {
+
+ // the longest supported digest is 512 bits (SHA-512)
+ private static final int RAW_RSA_MAX = 64;
+
+ private final byte[] precomputedDigest;
+ private int offset = 0;
+
+ public Raw() {
+ precomputedDigest = new byte[RAW_RSA_MAX];
+ }
+
+ // Stores the precomputed message digest value.
+ @Override
+ protected void engineUpdate(byte b) throws SignatureException {
+ if (offset >= precomputedDigest.length) {
+ offset = RAW_RSA_MAX + 1;
+ return;
+ }
+ precomputedDigest[offset++] = b;
+ }
+
+ // Stores the precomputed message digest value.
+ @Override
+ protected void engineUpdate(byte[] b, int off, int len)
+ throws SignatureException {
+ if (offset + len > precomputedDigest.length) {
+ offset = RAW_RSA_MAX + 1;
+ return;
+ }
+ System.arraycopy(b, off, precomputedDigest, offset, len);
+ offset += len;
+ }
+
+ // Stores the precomputed message digest value.
+ @Override
+ protected void engineUpdate(ByteBuffer byteBuffer) {
+ int len = byteBuffer.remaining();
+ if (len <= 0) {
+ return;
+ }
+ if (offset + len > precomputedDigest.length) {
+ offset = RAW_RSA_MAX + 1;
+ return;
+ }
+ byteBuffer.get(precomputedDigest, offset, len);
+ offset += len;
+ }
+
+ @Override
+ protected void resetDigest(){
+ offset = 0;
+ }
+
+ // Returns the precomputed message digest value.
+ @Override
+ protected byte[] getDigestValue() throws SignatureException {
+ if (offset > RAW_RSA_MAX) {
+ throw new SignatureException("Message digest is too long");
+ }
+
+ // Determine the digest algorithm from the digest length
+ if (offset == 20) {
+ setDigestName("SHA1");
+ } else if (offset == 36) {
+ setDigestName("SHA1+MD5");
+ } else if (offset == 32) {
+ setDigestName("SHA-256");
+ } else if (offset == 48) {
+ setDigestName("SHA-384");
+ } else if (offset == 64) {
+ setDigestName("SHA-512");
+ } else if (offset == 16) {
+ setDigestName("MD5");
+ } else {
+ throw new SignatureException(
+ "Message digest length is not supported");
+ }
+
+ byte[] result = new byte[offset];
+ System.arraycopy(precomputedDigest, 0, result, 0, offset);
+ offset = 0;
+
+ return result;
+ }
+ }
+
public static final class SHA1 extends RSASignature {
public SHA1() {
super("SHA1");
@@ -204,18 +305,22 @@
/**
* Resets the message digest if needed.
*/
- private void resetDigest() {
+ protected void resetDigest() {
if (needsReset) {
messageDigest.reset();
needsReset = false;
}
}
- private byte[] getDigestValue() {
+ protected byte[] getDigestValue() throws SignatureException {
needsReset = false;
return messageDigest.digest();
}
+ protected void setDigestName(String name) {
+ messageDigestAlgorithm = name;
+ }
+
/**
* Updates the data to be signed or verified
* using the specified byte.
@@ -277,9 +382,12 @@
byte[] hash = getDigestValue();
+ // Omit the hash OID when generating a Raw signature
+ boolean noHashOID = this instanceof Raw;
+
// Sign hash using MS Crypto APIs
- byte[] result = signHash(hash, hash.length,
+ byte[] result = signHash(noHashOID, hash, hash.length,
messageDigestAlgorithm, privateKey.getHCryptProvider(),
privateKey.getHCryptKey());
@@ -308,8 +416,8 @@
* Sign hash using Microsoft Crypto API with HCRYPTKEY.
* The returned data is in little-endian.
*/
- private native static byte[] signHash(byte[] hash, int hashSize,
- String hashAlgorithm, long hCryptProv, long hCryptKey)
+ private native static byte[] signHash(boolean noHashOID, byte[] hash,
+ int hashSize, String hashAlgorithm, long hCryptProv, long hCryptKey)
throws SignatureException;
/**
--- a/jdk/src/windows/native/sun/security/mscapi/security.cpp Thu Apr 28 10:14:12 2011 -0700
+++ b/jdk/src/windows/native/sun/security/mscapi/security.cpp Fri Apr 29 00:21:54 2011 +0100
@@ -81,6 +81,8 @@
(strcmp("SHA-1", pszHashAlgorithm) == 0)) {
algId = CALG_SHA1;
+ } else if (strcmp("SHA1+MD5", pszHashAlgorithm) == 0) {
+ algId = CALG_SSL3_SHAMD5; // a 36-byte concatenation of SHA-1 and MD5
} else if (strcmp("SHA-256", pszHashAlgorithm) == 0) {
algId = CALG_SHA_256;
} else if (strcmp("SHA-384", pszHashAlgorithm) == 0) {
@@ -473,11 +475,12 @@
/*
* Class: sun_security_mscapi_RSASignature
* Method: signHash
- * Signature: ([BILjava/lang/String;JJ)[B
+ * Signature: (Z[BILjava/lang/String;JJ)[B
*/
JNIEXPORT jbyteArray JNICALL Java_sun_security_mscapi_RSASignature_signHash
- (JNIEnv *env, jclass clazz, jbyteArray jHash, jint jHashSize,
- jstring jHashAlgorithm, jlong hCryptProv, jlong hCryptKey)
+ (JNIEnv *env, jclass clazz, jboolean noHashOID, jbyteArray jHash,
+ jint jHashSize, jstring jHashAlgorithm, jlong hCryptProv,
+ jlong hCryptKey)
{
HCRYPTHASH hHash = NULL;
jbyte* pHashBuffer = NULL;
@@ -548,14 +551,20 @@
// Determine size of buffer
DWORD dwBufLen = 0;
- if (::CryptSignHash(hHash, dwKeySpec, NULL, NULL, NULL, &dwBufLen) == FALSE)
+ DWORD dwFlags = 0;
+
+ if (noHashOID == JNI_TRUE) {
+ dwFlags = CRYPT_NOHASHOID; // omit hash OID in NONEwithRSA signature
+ }
+
+ if (::CryptSignHash(hHash, dwKeySpec, NULL, dwFlags, NULL, &dwBufLen) == FALSE)
{
ThrowException(env, SIGNATURE_EXCEPTION, GetLastError());
__leave;
}
pSignedHashBuffer = new jbyte[dwBufLen];
- if (::CryptSignHash(hHash, dwKeySpec, NULL, NULL, (BYTE*)pSignedHashBuffer, &dwBufLen) == FALSE)
+ if (::CryptSignHash(hHash, dwKeySpec, NULL, dwFlags, (BYTE*)pSignedHashBuffer, &dwBufLen) == FALSE)
{
ThrowException(env, SIGNATURE_EXCEPTION, GetLastError());
__leave;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/mscapi/SignUsingNONEwithRSA.java Fri Apr 29 00:21:54 2011 +0100
@@ -0,0 +1,225 @@
+/*
+ * 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 SignUsingNONEwithRSA.sh
+ */
+
+import java.security.*;
+import java.util.*;
+
+public class SignUsingNONEwithRSA {
+
+ private static final List<byte[]> precomputedHashes = Arrays.asList(
+ // A MD5 hash
+ new byte[] {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16
+ },
+ // A SHA-1 hash
+ new byte[] {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20
+ },
+ // A concatenation of SHA-1 and MD5 hashes (used during SSL handshake)
+ new byte[] {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30,
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36
+ },
+ // A SHA-256 hash
+ new byte[] {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30,
+ 0x31, 0x32
+ },
+ // A SHA-384 hash
+ new byte[] {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30,
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40,
+ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48
+ },
+ // A SHA-512 hash
+ new byte[] {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30,
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40,
+ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50,
+ 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x60,
+ 0x61, 0x62, 0x63, 0x64
+ });
+
+ private static List<byte[]> generatedSignatures = new ArrayList<>();
+
+ public static void main(String[] args) throws Exception {
+
+ Provider[] providers = Security.getProviders("Signature.NONEwithRSA");
+ if (providers == null) {
+ System.out.println("No JCE providers support the " +
+ "'Signature.NONEwithRSA' algorithm");
+ System.out.println("Skipping this test...");
+ return;
+
+ } else {
+ System.out.println("The following JCE providers support the " +
+ "'Signature.NONEwithRSA' algorithm: ");
+ for (Provider provider : providers) {
+ System.out.println(" " + provider.getName());
+ }
+ }
+ System.out.println("-------------------------------------------------");
+
+ KeyPair keys = getKeysFromKeyStore();
+ signAllUsing("SunMSCAPI", keys.getPrivate());
+ System.out.println("-------------------------------------------------");
+
+ verifyAllUsing("SunMSCAPI", keys.getPublic());
+ System.out.println("-------------------------------------------------");
+
+ verifyAllUsing("SunJCE", keys.getPublic());
+ System.out.println("-------------------------------------------------");
+
+ keys = generateKeys();
+ signAllUsing("SunJCE", keys.getPrivate());
+ System.out.println("-------------------------------------------------");
+
+ verifyAllUsing("SunMSCAPI", keys.getPublic());
+ System.out.println("-------------------------------------------------");
+
+ }
+
+ private static KeyPair getKeysFromKeyStore() throws Exception {
+ 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("6578658")) {
+ 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");
+ }
+
+ return new KeyPair(publicKey, privateKey);
+ }
+
+
+ private static KeyPair generateKeys() throws Exception {
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+ keyGen.initialize(1024, null);
+ KeyPair pair = keyGen.generateKeyPair();
+ PrivateKey privateKey = pair.getPrivate();
+ PublicKey publicKey = pair.getPublic();
+
+ if (privateKey == null || publicKey == null) {
+ throw new Exception("Cannot load the keys need to run this test");
+ }
+
+ return new KeyPair(publicKey, privateKey);
+ }
+
+ private static void signAllUsing(String providerName, PrivateKey privateKey)
+ throws Exception {
+ Signature sig1 = Signature.getInstance("NONEwithRSA", providerName);
+ if (sig1 == null) {
+ throw new Exception("'NONEwithRSA' is not supported");
+ }
+ if (sig1.getProvider() != null) {
+ System.out.println("Using NONEwithRSA signer from the " +
+ sig1.getProvider().getName() + " JCE provider");
+ } else {
+ System.out.println(
+ "Using NONEwithRSA signer from the internal JCE provider");
+ }
+
+ System.out.println("Using key: " + privateKey);
+ generatedSignatures.clear();
+ for (byte[] hash : precomputedHashes) {
+ sig1.initSign(privateKey);
+ sig1.update(hash);
+
+ try {
+
+ byte [] sigBytes = sig1.sign();
+ System.out.println("\nGenerated RSA signature over a " +
+ hash.length + "-byte hash (signature length: " +
+ sigBytes.length * 8 + " bits)");
+ System.out.println(String.format("0x%0" +
+ (sigBytes.length * 2) + "x",
+ new java.math.BigInteger(1, sigBytes)));
+ generatedSignatures.add(sigBytes);
+
+ } catch (SignatureException se) {
+ System.out.println("Error generating RSA signature: " + se);
+ }
+ }
+ }
+
+ private static void verifyAllUsing(String providerName, PublicKey publicKey)
+ throws Exception {
+ Signature sig1 = Signature.getInstance("NONEwithRSA", providerName);
+ if (sig1.getProvider() != null) {
+ System.out.println("\nUsing NONEwithRSA verifier from the " +
+ sig1.getProvider().getName() + " JCE provider");
+ } else {
+ System.out.println(
+ "\nUsing NONEwithRSA verifier from the internal JCE provider");
+ }
+
+ System.out.println("Using key: " + publicKey);
+
+ int i = 0;
+ for (byte[] hash : precomputedHashes) {
+
+ byte[] sigBytes = generatedSignatures.get(i++);
+ System.out.println("\nVerifying RSA Signature over a " +
+ hash.length + "-byte hash (signature length: " +
+ sigBytes.length * 8 + " bits)");
+ System.out.println(String.format("0x%0" +
+ (sigBytes.length * 2) + "x",
+ new java.math.BigInteger(1, sigBytes)));
+
+ sig1.initVerify(publicKey);
+ sig1.update(hash);
+ if (sig1.verify(sigBytes)) {
+ System.out.println("Verify PASSED");
+ } else {
+ throw new Exception("Verify FAILED");
+ }
+ }
+ }
+}