/*
 * Copyright 2019 IBM All Rights Reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

package org.hyperledger.fabric.gateway;

import java.io.IOException;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.Base64;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

public class IdentitiesTest {
    private static final TestUtils testUtils = TestUtils.getInstance();

    private final X509Credentials credentials = new X509Credentials();
    private final String x509CertificatePem = "-----BEGIN CERTIFICATE-----\n" +
            "MIICUzCCAfigAwIBAgIRAMTFlV/y/dOhwGhN74HVtz4wCgYIKoEcz1UBg3UwczEL\n" +
            "MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG\n" +
            "cmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\n" +
            "Lm9yZzEuZXhhbXBsZS5jb20wHhcNMjExMjE2MDgwNTAwWhcNMzExMjE0MDgwNTAw\n" +
            "WjBzMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN\n" +
            "U2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEcMBoGA1UE\n" +
            "AxMTY2Eub3JnMS5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IA\n" +
            "BPUXGKNMi5ijr9tAIISxhcZ45cMiOew/DcfdL5Rq4lUuvyTer25kSaz5nevc031i\n" +
            "l4jgj7nU+Iw65wG5pUBkCGKjbTBrMA4GA1UdDwEB/wQEAwIBpjAdBgNVHSUEFjAU\n" +
            "BggrBgEFBQcDAgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zApBgNVHQ4EIgQg\n" +
            "q/DDMh4h18KWJZNIV2htG3VKOUI9buJM/VJaCCESnZAwCgYIKoEcz1UBg3UDSQAw\n" +
            "RgIhAOS6cFARui9Uu8nqxRI832QDyh0MOi4wiFUDmj4tDywJAiEA1w9bWQqhoOxH\n" +
            "pV8IlmEFOv1vuYOonZ89aMcGnf7JYWo=\n" +
            "-----END CERTIFICATE-----";
    private final String pkcs8PrivateKeyPem = "-----BEGIN PRIVATE KEY-----\n" +
            "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgroLi/p5KU6nzlI/5\n" +
            "3ZUajHHMtP/X11Keo1D+FEk5RKugCgYIKoEcz1UBgi2hRANCAATADdoTyvHfjuB8\n" +
            "DGWjALZnzuzhn0kD/SMAjKqi/dBXBqCYJs+xWnO0ZYOLENxmeUa2Jc+QLXvwl+Cs\n" +
            "PORwuS3Q\n" +
            "-----END PRIVATE KEY-----";

    @Test
    public void certificate_read_error_throws_IOException() {
        String failMessage = "read failure";
        Reader reader = testUtils.newFailingReader(failMessage);

        assertThatThrownBy(() -> Identities.readX509Certificate(reader))
                .isInstanceOf(IOException.class)
                .hasMessage(failMessage);
    }

    @Test
    public void bad_certificate_PEM_throws_CertificateException() {
        assertThatThrownBy(() -> Identities.readX509Certificate("Invalid PEM"))
                .isInstanceOf(CertificateException.class);
    }

    @Test
    public void bad_certificate_throws_CertificateException() {
        String pem = "-----BEGIN CERTIFICATE-----\n" +
                Base64.getEncoder().encodeToString("Invalid certificate".getBytes(StandardCharsets.UTF_8)) + "\n" +
                "-----END CERTIFICATE-----";

        assertThatThrownBy(() -> Identities.readX509Certificate(pem))
                .isInstanceOf(CertificateException.class);
    }

    @Test
    public void private_key_read_error_throws_IOException() {
        String failMessage = "read failure";
        Reader reader = testUtils.newFailingReader(failMessage);

        assertThatThrownBy(() -> Identities.readPrivateKey(reader))
                .isInstanceOf(IOException.class)
                .hasMessage(failMessage);
    }

    @Test
    public void bad_private_key_PEM_throws_InvalidKeyException() {
        assertThatThrownBy(() -> Identities.readPrivateKey("Invalid PEM"))
                .isInstanceOf(InvalidKeyException.class);
    }

    @Test
    public void bad_private_key_throws_InvalidKeyException() {
        String pem = "-----BEGIN PRIVATE KEY-----\n" +
                Base64.getEncoder().encodeToString("Invalid private key".getBytes(StandardCharsets.UTF_8)) + "\n" +
                "-----END PRIVATE KEY-----";

        assertThatThrownBy(() -> Identities.readPrivateKey(pem))
                .isInstanceOf(InvalidKeyException.class);
    }

    @Test
    public void read_and_write_X509_certificate_PEM() throws CertificateException {
        Certificate certificate = Identities.readX509Certificate(x509CertificatePem);
        String result = Identities.toPemString(certificate);

        assertThat(result).isEqualToIgnoringNewLines(x509CertificatePem);
    }

    @Test
    public void read_and_write_PKCS8_private_key_PEM() throws InvalidKeyException {
        PrivateKey privateKey = Identities.readPrivateKey(pkcs8PrivateKeyPem);
        String result = Identities.toPemString(privateKey);

        assertThat(result).isEqualToIgnoringNewLines(pkcs8PrivateKeyPem);
    }

    @Test
    public void write_and_read_X509_certificate() throws CertificateException {
        Certificate expected = credentials.getCertificate();
        String pem = Identities.toPemString(expected);
        Certificate actual = Identities.readX509Certificate(pem);

        assertThat(actual).isEqualTo(expected);
    }

    @Test
    public void write_and_read_private_key() throws InvalidKeyException {
        PrivateKey expected = credentials.getPrivateKey();
        String pem = Identities.toPemString(expected);
        PrivateKey actual = Identities.readPrivateKey(pem);

        assertThat(actual).isEqualTo(expected);
    }
}
