Enforce base64 encoding for netconf-keystore
[netconf.git] / plugins / netconf-client-mdsal / src / test / java / org / opendaylight / netconf / client / mdsal / impl / DefaultSslContextFactoryProviderTest.java
1 /*
2  * Copyright (c) 2018 ZTE Corporation. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netconf.client.mdsal.impl;
9
10 import static org.hamcrest.CoreMatchers.startsWith;
11 import static org.hamcrest.MatcherAssert.assertThat;
12 import static org.junit.Assert.assertThrows;
13 import static org.junit.Assert.assertTrue;
14 import static org.mockito.ArgumentMatchers.any;
15 import static org.mockito.Mockito.doAnswer;
16 import static org.mockito.Mockito.doReturn;
17
18 import java.nio.charset.StandardCharsets;
19 import java.security.KeyStoreException;
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.Set;
23 import org.junit.jupiter.api.AfterEach;
24 import org.junit.jupiter.api.BeforeEach;
25 import org.junit.jupiter.api.Test;
26 import org.junit.jupiter.api.extension.ExtendWith;
27 import org.mockito.Mock;
28 import org.mockito.junit.jupiter.MockitoExtension;
29 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
30 import org.opendaylight.mdsal.binding.api.DataBroker;
31 import org.opendaylight.mdsal.binding.api.DataObjectModification;
32 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
33 import org.opendaylight.mdsal.binding.api.DataTreeModification;
34 import org.opendaylight.mdsal.binding.api.RpcProviderService;
35 import org.opendaylight.mdsal.singleton.api.ClusterSingletonServiceProvider;
36 import org.opendaylight.netconf.api.xml.XmlUtil;
37 import org.opendaylight.netconf.keystore.legacy.impl.DefaultNetconfKeystoreService;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev231109.Keystore;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev231109._private.keys.PrivateKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev231109._private.keys.PrivateKeyBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev231109._private.keys.PrivateKeyKey;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev231109.trusted.certificates.TrustedCertificate;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev231109.trusted.certificates.TrustedCertificateBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev231109.trusted.certificates.TrustedCertificateKey;
45 import org.opendaylight.yangtools.concepts.Registration;
46 import org.w3c.dom.Document;
47 import org.w3c.dom.Element;
48
49 @ExtendWith(MockitoExtension.class)
50 class DefaultSslContextFactoryProviderTest {
51     private static final String XML_ELEMENT_PRIVATE_KEY = "private-key";
52     private static final String XML_ELEMENT_NAME = "name";
53     private static final String XML_ELEMENT_DATA = "data";
54     private static final String XML_ELEMENT_CERT_CHAIN = "certificate-chain";
55     private static final String XML_ELEMENT_TRUSTED_CERT = "trusted-certificate";
56     private static final String XML_ELEMENT_CERT = "certificate";
57
58     @Mock
59     private DataBroker dataBroker;
60     @Mock
61     private RpcProviderService rpcProvider;
62     @Mock
63     private ClusterSingletonServiceProvider cssProvider;
64     @Mock
65     private AAAEncryptionService encryptionService;
66     @Mock
67     private Registration registration;
68     @Mock
69     private DataTreeModification<Keystore> dataTreeModification1;
70     @Mock
71     private DataTreeModification<Keystore> dataTreeModification2;
72     @Mock
73     private DataObjectModification<Keystore> keystoreObjectModification1;
74     @Mock
75     private DataObjectModification<Keystore> keystoreObjectModification2;
76     @Mock
77     private DataObjectModification<PrivateKey> privateKeyModification;
78     @Mock
79     private DataObjectModification<TrustedCertificate> trustedCertificateModification;
80
81     private DataTreeChangeListener<Keystore> listener;
82     private DefaultNetconfKeystoreService keystore;
83
84     @BeforeEach
85     void beforeEach() {
86         doAnswer(inv -> {
87             listener = inv.getArgument(1);
88             return registration;
89         }).when(dataBroker).registerTreeChangeListener(any(), any());
90         doReturn(registration).when(cssProvider).registerClusterSingletonService(any());
91         keystore = new DefaultNetconfKeystoreService(dataBroker, rpcProvider, cssProvider, encryptionService);
92     }
93
94     @AfterEach
95     void afterEach() {
96         keystore.close();
97     }
98
99     private DefaultSslContextFactoryProvider newProvider() {
100         return new DefaultSslContextFactoryProvider(keystore);
101     }
102
103     @Test
104     void testKeystoreAdapterInit() throws Exception {
105         try (var keystoreAdapter = newProvider()) {
106             final var ex = assertThrows(KeyStoreException.class, () -> keystoreAdapter.getJavaKeyStore(Set.of()));
107             assertThat(ex.getMessage(), startsWith("No keystore private key found"));
108         }
109     }
110
111     @Test
112     void testWritePrivateKey() throws Exception {
113         doReturn(keystoreObjectModification1).when(dataTreeModification1).getRootNode();
114         doReturn(List.of(privateKeyModification)).when(keystoreObjectModification1)
115             .getModifiedChildren(PrivateKey.class);
116         doReturn(DataObjectModification.ModificationType.WRITE).when(privateKeyModification).modificationType();
117
118         final var privateKey = getPrivateKey();
119         doReturn(privateKey).when(privateKeyModification).dataAfter();
120
121         try (var keystoreAdapter = newProvider()) {
122             listener.onDataTreeChanged(List.of(dataTreeModification1));
123
124             final var keyStore = keystoreAdapter.getJavaKeyStore(Set.of());
125             assertTrue(keyStore.containsAlias(privateKey.getName()));
126         }
127     }
128
129     @Test
130     void testWritePrivateKeyAndTrustedCertificate() throws Exception {
131         // Prepare PrivateKey configuration
132         doReturn(keystoreObjectModification1).when(dataTreeModification1).getRootNode();
133
134         doReturn(List.of(privateKeyModification)).when(keystoreObjectModification1)
135             .getModifiedChildren(PrivateKey.class);
136         doReturn(DataObjectModification.ModificationType.WRITE).when(privateKeyModification).modificationType();
137
138         final var privateKey = getPrivateKey();
139         doReturn(privateKey).when(privateKeyModification).dataAfter();
140
141         // Prepare TrustedCertificate configuration
142         doReturn(keystoreObjectModification2).when(dataTreeModification2).getRootNode();
143
144         doReturn(List.of()).when(keystoreObjectModification2).getModifiedChildren(PrivateKey.class);
145         doReturn(List.of(trustedCertificateModification)).when(keystoreObjectModification2)
146             .getModifiedChildren(TrustedCertificate.class);
147         doReturn(DataObjectModification.ModificationType.WRITE).when(trustedCertificateModification).modificationType();
148
149         final var trustedCertificate = getTrustedCertificate();
150         doReturn(trustedCertificate).when(trustedCertificateModification).dataAfter();
151
152         try (var keystoreAdapter = newProvider()) {
153             // Apply configurations
154             listener.onDataTreeChanged(List.of(dataTreeModification1, dataTreeModification2));
155
156             // Check result
157             final var keyStore = keystoreAdapter.getJavaKeyStore(Set.of());
158             assertTrue(keyStore.containsAlias(privateKey.getName()));
159             assertTrue(keyStore.containsAlias(trustedCertificate.getName()));
160         }
161     }
162
163     private static PrivateKey getPrivateKey() throws Exception {
164         final var privateKeys = new ArrayList<PrivateKey>();
165         final var document = readKeystoreXML();
166         final var nodeList = document.getElementsByTagName(XML_ELEMENT_PRIVATE_KEY);
167         for (int i = 0; i < nodeList.getLength(); i++) {
168             if (nodeList.item(i) instanceof Element element) {
169                 final var keyName = element.getElementsByTagName(XML_ELEMENT_NAME).item(0).getTextContent();
170                 final var keyData = element.getElementsByTagName(XML_ELEMENT_DATA).item(0).getTextContent()
171                     .getBytes(StandardCharsets.UTF_8);
172                 final var certNodes = element.getElementsByTagName(XML_ELEMENT_CERT_CHAIN);
173                 final var certChain = new ArrayList<byte[]>();
174                 for (int j = 0; j < certNodes.getLength(); j++) {
175                     if (certNodes.item(j) instanceof Element certNode) {
176                         certChain.add(certNode.getTextContent().getBytes(StandardCharsets.UTF_8));
177                     }
178                 }
179
180                 privateKeys.add(new PrivateKeyBuilder()
181                     .withKey(new PrivateKeyKey(keyName))
182                     .setName(keyName)
183                     .setData(keyData)
184                     .setCertificateChain(certChain)
185                     .build());
186             }
187         }
188
189         return privateKeys.get(0);
190     }
191
192     private static TrustedCertificate getTrustedCertificate() throws Exception {
193         final var trustedCertificates = new ArrayList<TrustedCertificate>();
194         final var document = readKeystoreXML();
195         final var nodeList = document.getElementsByTagName(XML_ELEMENT_TRUSTED_CERT);
196         for (int i = 0; i < nodeList.getLength(); i++) {
197             if (nodeList.item(i) instanceof Element element) {
198                 final var certName = element.getElementsByTagName(XML_ELEMENT_NAME).item(0).getTextContent();
199                 final var certData = element.getElementsByTagName(XML_ELEMENT_CERT).item(0).getTextContent()
200                     .getBytes(StandardCharsets.UTF_8);
201
202                 trustedCertificates.add(new TrustedCertificateBuilder()
203                     .withKey(new TrustedCertificateKey(certName))
204                     .setName(certName)
205                     .setCertificate(certData)
206                     .build());
207             }
208         }
209
210         return trustedCertificates.get(0);
211     }
212
213     private static Document readKeystoreXML() throws Exception {
214         return XmlUtil.readXmlToDocument(
215             DefaultSslContextFactoryProviderTest.class.getResourceAsStream("/netconf-keystore.xml"));
216     }
217 }