]> git.basschouten.com Git - openhab-addons.git/blob
984a86ff1f73337a6cde817d5733f321edc4deb1
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.mqtt.internal.ssl;
14
15 import static org.hamcrest.CoreMatchers.is;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.mockito.ArgumentMatchers.eq;
18 import static org.mockito.Mockito.*;
19
20 import java.io.FileNotFoundException;
21 import java.io.InputStream;
22 import java.security.NoSuchAlgorithmException;
23 import java.security.PublicKey;
24 import java.security.cert.CertificateException;
25 import java.security.cert.CertificateFactory;
26 import java.security.cert.X509Certificate;
27
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.junit.jupiter.api.Test;
30 import org.openhab.core.util.HexUtils;
31
32 /**
33  * Tests cases for {@link PinTrustManager}.
34  *
35  * @author David Graeff - Initial contribution
36  */
37 @NonNullByDefault
38 public class PinningSSLContextProviderTest {
39
40     @Test
41     public void getDigestDataFor() throws NoSuchAlgorithmException, CertificateException, FileNotFoundException {
42         // Load test certificate
43         InputStream inputCert = getClass().getResourceAsStream("cert.pem");
44         X509Certificate certificate = (X509Certificate) CertificateFactory.getInstance("X.509")
45                 .generateCertificate(inputCert);
46
47         PinTrustManager pinTrustManager = new PinTrustManager();
48         PinMessageDigest pinMessageDigest = pinTrustManager.getMessageDigestForSigAlg(certificate.getSigAlgName());
49         String hashForCert = HexUtils
50                 .bytesToHex(pinMessageDigest.digest(pinTrustManager.getEncoded(PinType.CERTIFICATE_TYPE, certificate)));
51         String expectedHash = "41fa6d40d1e8f53ac81a395ac13b1efa10917718f1ebe3ac278925716d630b72".toUpperCase();
52         assertThat(hashForCert, is(expectedHash));
53
54         String hashForPublicKey = HexUtils
55                 .bytesToHex(pinMessageDigest.digest(pinTrustManager.getEncoded(PinType.PUBLIC_KEY_TYPE, certificate)));
56         String expectedPubKeyHash = "9a6f30e67ae9723579da2575c35daf7da3b370b04ac0bde031f5e1f5e4617eb8".toUpperCase();
57         assertThat(hashForPublicKey, is(expectedPubKeyHash));
58     }
59
60     // Test if X509Certificate.getEncoded() is called if it is a certificate pin and
61     // X509Certificate.getPublicKey().getEncoded() is called if it is a public key pinning.
62     @Test
63     public void certPinCallsX509CertificateGetEncoded() throws NoSuchAlgorithmException, CertificateException {
64         PinTrustManager pinTrustManager = new PinTrustManager();
65         pinTrustManager.addPinning(Pin.learningPin(PinType.CERTIFICATE_TYPE));
66
67         // Mock a certificate
68         X509Certificate certificate = mock(X509Certificate.class);
69         when(certificate.getEncoded()).thenReturn(new byte[0]);
70         when(certificate.getSigAlgName()).thenReturn("SHA256withRSA");
71
72         pinTrustManager.checkServerTrusted(new X509Certificate[] { certificate }, null);
73         verify(certificate).getEncoded();
74     }
75
76     // Test if X509Certificate.getEncoded() is called if it is a certificate pin and
77     // X509Certificate.getPublicKey().getEncoded() is called if it is a public key pinning.
78     @Test
79     public void pubKeyPinCallsX509CertificateGetPublicKey() throws NoSuchAlgorithmException, CertificateException {
80         PinTrustManager pinTrustManager = new PinTrustManager();
81         pinTrustManager.addPinning(Pin.learningPin(PinType.PUBLIC_KEY_TYPE));
82
83         // Mock a certificate
84         PublicKey publicKey = mock(PublicKey.class);
85         when(publicKey.getEncoded()).thenReturn(new byte[0]);
86
87         X509Certificate certificate = mock(X509Certificate.class);
88         when(certificate.getSigAlgName()).thenReturn("SHA256withRSA");
89         when(certificate.getPublicKey()).thenReturn(publicKey);
90
91         pinTrustManager.checkServerTrusted(new X509Certificate[] { certificate }, null);
92         verify(publicKey).getEncoded();
93     }
94
95     /**
96      * Overwrite {@link #getMessageDigestForSigAlg(String)} method and return a pre-defined {@link PinMessageDigest}.
97      */
98     public static class PinTrustManagerEx extends PinTrustManager {
99         private final PinMessageDigest pinMessageDigest;
100
101         PinTrustManagerEx(PinMessageDigest pinMessageDigest) {
102             this.pinMessageDigest = pinMessageDigest;
103         }
104
105         @Override
106         PinMessageDigest getMessageDigestForSigAlg(String sigAlg) throws CertificateException {
107             return pinMessageDigest;
108         }
109     }
110
111     @Test
112     public void learningMode() throws NoSuchAlgorithmException, CertificateException {
113         PinMessageDigest pinMessageDigest = new PinMessageDigest("SHA-256");
114         PinTrustManager pinTrustManager = new PinTrustManagerEx(pinMessageDigest);
115         byte[] testCert = { 1, 2, 3 };
116         byte[] digestOfTestCert = pinMessageDigest.digest(testCert);
117
118         // Add a certificate pin in learning mode to a trust manager
119         Pin pin = Pin.learningPin(PinType.CERTIFICATE_TYPE);
120         pinTrustManager.addPinning(pin);
121         assertThat(pinTrustManager.pins.size(), is(1));
122
123         // Mock a callback
124         PinnedCallback callback = mock(PinnedCallback.class);
125         pinTrustManager.setCallback(callback);
126
127         // Mock a certificate
128         X509Certificate certificate = mock(X509Certificate.class);
129         when(certificate.getEncoded()).thenReturn(testCert);
130         when(certificate.getSigAlgName()).thenReturn("SHA256withRSA");
131
132         // Perform an SSL certificate check
133         pinTrustManager.checkServerTrusted(new X509Certificate[] { certificate }, null);
134
135         // After a first connect learning mode should turn into check mode. It should have learned the hash data and
136         // message digest, returned by PinTrustManager.getMessageDigestForSigAlg().
137         assertThat(pin.learning, is(false));
138         assertThat(pin.pinData, is(digestOfTestCert));
139         assertThat(pin.hashDigest, is(pinMessageDigest));
140         // We expect callbacks
141         verify(callback).pinnedLearnedHash(eq(pin));
142         verify(callback).pinnedConnectionAccepted();
143     }
144
145     @Test
146     public void checkMode() throws NoSuchAlgorithmException, CertificateException {
147         PinTrustManager pinTrustManager = new PinTrustManager();
148         PinMessageDigest pinMessageDigest = new PinMessageDigest("SHA-256");
149         byte[] testCert = { 1, 2, 3 };
150         byte[] digestOfTestCert = pinMessageDigest.digest(testCert);
151
152         // Add a certificate pin in checking mode to a trust manager
153         Pin pin = Pin.checkingPin(PinType.CERTIFICATE_TYPE, pinMessageDigest, digestOfTestCert);
154         pinTrustManager.addPinning(pin);
155         assertThat(pinTrustManager.pins.size(), is(1));
156
157         // Mock a callback
158         PinnedCallback callback = mock(PinnedCallback.class);
159         pinTrustManager.setCallback(callback);
160
161         // Mock a certificate
162         X509Certificate certificate = mock(X509Certificate.class);
163         when(certificate.getEncoded()).thenReturn(testCert);
164         when(certificate.getSigAlgName()).thenReturn("SHA256withRSA");
165
166         // Perform an SSL certificate check
167         pinTrustManager.checkServerTrusted(new X509Certificate[] { certificate }, null);
168
169         // After a first connect learning mode should turn into check mode
170         assertThat(pin.learning, is(false));
171         assertThat(pin.pinData, is(digestOfTestCert));
172         assertThat(pin.hashDigest, is(pinMessageDigest));
173         // We expect callbacks
174         verify(callback, times(0)).pinnedLearnedHash(eq(pin));
175         verify(callback).pinnedConnectionAccepted();
176     }
177 }