/*
 * 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");
            }
        }
    }
}
