org.eclipse.smarthome.io.net.http.internal.ExtensibleTrustManagerImplTest.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.smarthome.io.net.http.internal.ExtensibleTrustManagerImplTest.java

Source

/**
 * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 */
package org.eclipse.smarthome.io.net.http.internal;

import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;

import java.net.Socket;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.security.auth.x500.X500Principal;

import org.apache.commons.lang.reflect.FieldUtils;
import org.eclipse.smarthome.io.net.http.TlsTrustManagerProvider;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;

/**
 * Tests which validate the behavior of the ExtensibleTrustManager
 *
 * @author Martin van Wingerden - Initial contribution
 */
public class ExtensibleTrustManagerImplTest {
    private ExtensibleTrustManagerImpl subject;

    @Mock
    private TlsTrustManagerProvider trustmanagerProvider;

    @Mock
    private TlsTrustManagerProvider trustmanagerProviderHostPort;

    @Mock
    private X509ExtendedTrustManager trustmanager;

    @Mock
    private X509ExtendedTrustManager trustmanager2;

    @Mock
    private X509ExtendedTrustManager defaultTrustManager;

    @Mock
    private SSLEngine sslEngine;

    @Mock
    private X509Certificate topOfChain;

    @Mock
    private X509Certificate bottomOfChain;

    private X509Certificate[] chain;

    @Before
    public void setup() {
        initMocks(this);

        when(trustmanagerProvider.getHostName()).thenReturn("example.org");
        when(trustmanagerProvider.getTrustManager()).thenReturn(trustmanager);

        when(trustmanagerProviderHostPort.getHostName()).thenReturn("example.org:443");
        when(trustmanagerProviderHostPort.getTrustManager()).thenReturn(trustmanager2);

        subject = new ExtensibleTrustManagerImpl();
        subject.addTlsTrustManagerProvider(trustmanagerProvider);
        subject.addTlsTrustManagerProvider(trustmanagerProviderHostPort);

        chain = new X509Certificate[] { topOfChain, bottomOfChain };
    }

    @Test
    public void shouldForwardCallsToMockForMatchingCN() throws CertificateException {
        when(topOfChain.getSubjectX500Principal())
                .thenReturn(new X500Principal("CN=example.org, OU=Smarthome, O=Eclipse, C=DE"));

        subject.checkServerTrusted(chain, "just");

        verify(trustmanager).checkServerTrusted(chain, "just", (Socket) null);
        verifyNoMoreInteractions(trustmanager, trustmanager2);
    }

    @Test
    public void shouldForwardCallsToMockForMatchingHost() throws CertificateException {
        when(sslEngine.getPeerHost()).thenReturn("example.org");
        when(sslEngine.getPeerPort()).thenReturn(443);

        subject.checkServerTrusted(chain, "just", sslEngine);

        verify(trustmanager2).checkServerTrusted(chain, "just", sslEngine);
        verifyNoMoreInteractions(trustmanager, trustmanager2);
    }

    @Test
    public void shouldForwardCallsToMockForMatchingAlternativeNames() throws CertificateException {
        when(topOfChain.getSubjectX500Principal())
                .thenReturn(new X500Principal("CN=example.com, OU=Smarthome, O=Eclipse, C=DE"));
        when(topOfChain.getSubjectAlternativeNames())
                .thenReturn(constructAlternativeNames("example1.com", "example.org"));

        subject.checkClientTrusted(chain, "just");

        verify(trustmanager).checkClientTrusted(chain, "just", (Socket) null);
        verifyNoMoreInteractions(trustmanager);
    }

    @Test
    public void shouldBeResilientAgainstNullSubjectAlternativeNames()
            throws CertificateException, IllegalAccessException {
        FieldUtils.writeField(subject, "defaultTrustManager", defaultTrustManager, true);

        when(topOfChain.getSubjectX500Principal())
                .thenReturn(new X500Principal("CN=example.com, OU=Smarthome, O=Eclipse, C=DE"));
        when(topOfChain.getSubjectAlternativeNames()).thenReturn(null);

        subject.checkClientTrusted(chain, "just");

        verify(defaultTrustManager).checkClientTrusted(chain, "just", (Socket) null);
        verifyNoMoreInteractions(trustmanager);
    }

    @Test
    public void shouldBeResilientAgainstMissingCommonNames() throws CertificateException, IllegalAccessException {
        FieldUtils.writeField(subject, "defaultTrustManager", defaultTrustManager, true);

        when(topOfChain.getSubjectX500Principal()).thenReturn(new X500Principal("OU=Smarthome, O=Eclipse, C=DE"));

        subject.checkClientTrusted(chain, "just");

        verify(defaultTrustManager).checkClientTrusted(chain, "just", (Socket) null);
        verifyNoMoreInteractions(trustmanager);
    }

    @Test
    public void shouldBeResilientAgainstInvalidCertificates() throws CertificateException, IllegalAccessException {
        FieldUtils.writeField(subject, "defaultTrustManager", defaultTrustManager, true);

        when(topOfChain.getSubjectX500Principal())
                .thenReturn(new X500Principal("CN=example.com, OU=Smarthome, O=Eclipse, C=DE"));
        when(topOfChain.getSubjectAlternativeNames())
                .thenThrow(new CertificateParsingException("Invalid certificate!!!"));

        subject.checkClientTrusted(chain, "just");

        verify(defaultTrustManager).checkClientTrusted(chain, "just", (Socket) null);
        verifyNoMoreInteractions(trustmanager);
    }

    @Test
    public void shouldNotForwardCallsToMockForDifferentCN() throws CertificateException, IllegalAccessException {
        FieldUtils.writeField(subject, "defaultTrustManager", defaultTrustManager, true);
        mockSubjectForCertificate(topOfChain, "CN=example.com, OU=Smarthome, O=Eclipse, C=DE");
        mockIssuerForCertificate(topOfChain, "CN=Eclipse, OU=Smarthome, O=Eclipse, C=DE");
        mockSubjectForCertificate(bottomOfChain, "CN=Eclipse, OU=Smarthome, O=Eclipse, C=DE");
        mockIssuerForCertificate(bottomOfChain, "");
        when(topOfChain.getEncoded()).thenReturn(new byte[0]);

        subject.checkServerTrusted(chain, "just");

        verify(defaultTrustManager).checkServerTrusted(chain, "just", (Socket) null);
        verifyZeroInteractions(trustmanager);
    }

    private Collection<List<?>> constructAlternativeNames(String... alternatives) {
        Collection<List<?>> alternativeNames = new ArrayList<>();
        for (String alternative : alternatives) {
            alternativeNames.add(Stream.of(0, alternative).collect(Collectors.toList()));
        }

        return alternativeNames;
    }

    private void mockSubjectForCertificate(X509Certificate certificate, String principal) {
        when(certificate.getSubjectX500Principal()).thenReturn(new X500Principal(principal));

    }

    private void mockIssuerForCertificate(X509Certificate certificate, String principal) {
        when(certificate.getIssuerX500Principal()).thenReturn(new X500Principal(principal));
    }
}