/*
 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

import java.net.SocketTimeoutException;
import java.security.PrivateKey;
import java.security.cert.CertPathValidatorException;
import java.security.cert.X509Certificate;
import java.io.File;

/**
 * Test for incorrect value of com.sun.security.ocsp.timeout system property.
 * The test checks that incorrect values are processed correctly: OCSP timeout
 * should be set to default value.
 *
 * Test scenario: - a certificate is good - an application tries to check
 * revocation status of certificate via OCSP - com.sun.security.ocsp.timeout
 * system property is set to incorrect value - first, OCSP responder does not
 * delay responses, successful validation is expected - next, OCSP responder
 * delays responses, validation failure is expected
 *
 */

/*
 * @test
 * @author Shivangi Gupta
 * @library ../../common ../../common/bcpkix-jdk15on.jar ../../common/bcprov-jdk15on.jar
 * @clean *
 * @run main/othervm/policy=httpTimeNotpolicy -Djava.security.manager
 * OCSPTimeoutTest string
 * 
 * @run main/othervm/policy=httpTimeNotpolicy -Djava.security.manager
 * OCSPTimeoutTest 2147483648
 * 
 * @run main/othervm/policy=httpTimeNotpolicy -Djava.security.manager
 * OCSPTimeoutTest
 *
 * @run main/othervm/policy=httpTimeNotpolicy -Djava.security.manager
 * OCSPTimeoutTest -1
 */
public class OCSPTimeoutTest {

    private static final String WORK_DIR = System.getProperty("test.src", ".");
    private X509Certificate cert;
    private X509Certificate trustedCert;
    private PrivateKey ocspResponderKey;
    private String ocspTimeout;

    private final int ocspResponderDelay = 30;

    public static void main(String[] args) throws Exception {
        OCSPTimeoutTest test = new OCSPTimeoutTest();
        test.runTest(args);
    }

    private void parseArgs(String[] args) throws Exception {
        String BASE = WORK_DIR.substring(0, WORK_DIR.indexOf("RevocationChecking") + "RevocationChecking".length());
        String certFile = BASE + File.separator + "common" + File.separator + "data" + File.separator + "ca"
                + File.separator + "valid.pem";
        String trustedCertFile = BASE + File.separator + "common" + File.separator + "data" + File.separator + "ca"
                + File.separator + "root.pem";
        String ocspResponderKeyFile = BASE + File.separator + "common" + File.separator + "data" + File.separator + "ca"
                + File.separator + "ocsp_responder_key";
        cert = Utils.getCertFromFile(certFile);
        trustedCert = Utils.getCertFromFile(trustedCertFile);
        ocspResponderKey = Utils.loadRsaPrivateKey(ocspResponderKeyFile);
        
        if (args.length >= 1) {
            ocspTimeout = args[0];
        }
        
        if (cert == null) {
            throw new IllegalArgumentException("Certificate is not set");
        }

        if (trustedCert == null) {
            throw new IllegalArgumentException("Trusted certificate is not set");
        }

        if (ocspResponderKey == null) {
            throw new IllegalArgumentException("OCSP responder key is not set");
        }
    }

    /**
     * @param args Command line parameters
     */
    private void runTest(String[] args) throws Exception {
        parseArgs(args);

        // Set maximum allowable OCSP connection timeout
        if (ocspTimeout != null) {
            System.setProperty("com.sun.security.ocsp.timeout", ocspTimeout);
        }
        System.out.println("com.sun.security.ocsp.timeout" + System.getProperty("com.sun.security.ocsp.timeout"));

        // Start OCSP responder
        SimpleOCSPResponderDataBase ocspResponderDatabase = new SimpleOCSPResponderDataBase();
        ocspResponderDatabase.addGood(cert.getSerialNumber());
        int simpleOcspResponderPort = Utils.getFreePort();
        SimpleOCSPResponder simpleOcspResponder = new SimpleOCSPResponder(simpleOcspResponderPort,
                ocspResponderDatabase, "SHA1withRSA", ocspResponderKey);
        simpleOcspResponder.setDaemon(true);
        simpleOcspResponder.start();

        Utils.sleep(5);

        System.out.println("Testing with no delay on OCSP responder");
        System.out.println("Expected result: successful validation");
        Utils.doOcspValidation(cert, trustedCert, simpleOcspResponderPort);

        try {
            System.out.println("Testing with delay on OCSP responder");
            System.out.println("Expected result: validation failure (CertPathValidatorException)");

            simpleOcspResponder.setDelay(ocspResponderDelay);

            long start = System.currentTimeMillis();
            Utils.doOcspValidation(cert, trustedCert, simpleOcspResponderPort);
            long time = System.currentTimeMillis() - start;

            throw new Exception("Test failed: CertPathValidatorException was not thrown (validation took " + time / 1000
                    + " seconds)");
        } catch (CertPathValidatorException e) {
            System.out.println("Got CertPathValidatorException" + e.getMessage());
            System.out.println("Expected CertPathValidatorException was thrown");
            Throwable cause = e.getCause();
            if (cause == null || !(cause instanceof SocketTimeoutException)) {
                e.printStackTrace();
                throw new Exception("CertPathValidatorException was not caused by SocketTimeoutException");
            }
            System.out.println(
                    "Test passed: expected CertPathValidatorException that was caused by SocketTimeoutException");
        }
    }
}
