2 * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.netconf.client.mdsal.impl;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.annotations.VisibleForTesting;
13 import com.google.common.collect.ImmutableClassToInstanceMap;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import com.google.common.util.concurrent.MoreExecutors;
18 import com.google.common.util.concurrent.SettableFuture;
19 import java.nio.charset.StandardCharsets;
20 import java.security.GeneralSecurityException;
21 import java.util.ArrayList;
22 import java.util.Base64;
23 import javax.annotation.PreDestroy;
24 import javax.inject.Inject;
25 import javax.inject.Singleton;
26 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
27 import org.opendaylight.mdsal.binding.api.DataBroker;
28 import org.opendaylight.mdsal.binding.api.RpcProviderService;
29 import org.opendaylight.mdsal.binding.api.WriteTransaction;
30 import org.opendaylight.mdsal.common.api.CommitInfo;
31 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
32 import org.opendaylight.mdsal.dom.api.DefaultDOMRpcException;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntry;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntryInput;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntryOutput;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntryOutputBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKey;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKeyInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKeyOutput;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddPrivateKeyOutputBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificate;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificateInput;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificateOutput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddTrustedCertificateOutputBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.Keystore;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntry;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntryInput;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntryOutput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntryOutputBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKey;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKeyInput;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKeyOutput;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemovePrivateKeyOutputBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificate;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificateInput;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificateOutput;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveTrustedCertificateOutputBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017._private.keys.PrivateKeyKey;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredential;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredentialBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredentialKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificate;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.trusted.certificates.TrustedCertificateKey;
65 import org.opendaylight.yangtools.concepts.Registration;
66 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
67 import org.opendaylight.yangtools.yang.binding.Rpc;
68 import org.opendaylight.yangtools.yang.common.RpcResult;
69 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
70 import org.osgi.service.component.annotations.Activate;
71 import org.osgi.service.component.annotations.Component;
72 import org.osgi.service.component.annotations.Deactivate;
73 import org.osgi.service.component.annotations.Reference;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
78 @Component(service = { })
79 public final class NetconfSalKeystoreRpcs implements AutoCloseable {
80 private static final Logger LOG = LoggerFactory.getLogger(NetconfSalKeystoreRpcs.class);
81 private static final InstanceIdentifier<Keystore> KEYSTORE_IID = InstanceIdentifier.create(Keystore.class);
83 // FIXME: we are populating config datastore, but there may be risks with concurrent access. We really should be
84 // using cluster singleton service here.
85 private final DataBroker dataBroker;
86 private final AAAEncryptionService encryptionService;
87 private final Registration reg;
91 public NetconfSalKeystoreRpcs(@Reference final DataBroker dataBroker,
92 @Reference final AAAEncryptionService encryptionService, @Reference final RpcProviderService rpcProvider) {
93 this.dataBroker = requireNonNull(dataBroker);
94 this.encryptionService = requireNonNull(encryptionService);
96 reg = rpcProvider.registerRpcImplementations(ImmutableClassToInstanceMap.<Rpc<?, ?>>builder()
97 .put(RemoveKeystoreEntry.class, this::removeKeystoreEntry)
98 .put(AddKeystoreEntry.class, this::addKeystoreEntry)
99 .put(AddTrustedCertificate.class, this::addTrustedCertificate)
100 .put(RemoveTrustedCertificate.class, this::removeTrustedCertificate)
101 .put(AddPrivateKey.class, this::addPrivateKey)
102 .put(RemovePrivateKey.class, this::removePrivateKey)
104 LOG.info("NETCONF keystore service started");
110 public void close() {
112 LOG.info("NETCONF keystore service stopped");
115 private ListenableFuture<RpcResult<RemoveKeystoreEntryOutput>> removeKeystoreEntry(
116 final RemoveKeystoreEntryInput input) {
117 LOG.debug("Removing keypairs: {}", input);
119 final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
121 for (final String id : input.getKeyId()) {
122 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION,
123 KEYSTORE_IID.child(KeyCredential.class, new KeyCredentialKey(id)));
126 final SettableFuture<RpcResult<RemoveKeystoreEntryOutput>> rpcResult = SettableFuture.create();
128 writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
130 public void onSuccess(final CommitInfo result) {
131 LOG.debug("remove-key-pair success. Input: {}", input);
132 rpcResult.set(RpcResultBuilder.success(new RemoveKeystoreEntryOutputBuilder().build()).build());
136 public void onFailure(final Throwable throwable) {
137 LOG.warn("remove-key-pair failed. Input: {}", input, throwable);
138 rpcResult.setException(throwable);
140 }, MoreExecutors.directExecutor());
145 private ListenableFuture<RpcResult<AddKeystoreEntryOutput>> addKeystoreEntry(final AddKeystoreEntryInput input) {
146 LOG.debug("Adding keypairs: {}", input);
148 final var plain = input.nonnullKeyCredential();
149 final var encrypted = new ArrayList<KeyCredential>(plain.size());
150 for (var credential : plain.values()) {
152 encrypted.add(new KeyCredentialBuilder(credential)
153 .setPrivateKey(encryptString(credential.getPrivateKey()))
154 .setPassphrase(encryptString(credential.getPassphrase()))
156 } catch (GeneralSecurityException e) {
157 return Futures.immediateFailedFuture(new DefaultDOMRpcException("Failed to decrypt " + credential, e));
161 final var writeTransaction = dataBroker.newWriteOnlyTransaction();
162 for (var keypair : encrypted) {
163 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION,
164 KEYSTORE_IID.child(KeyCredential.class, keypair.key()), keypair);
167 final var rpcResult = SettableFuture.<RpcResult<AddKeystoreEntryOutput>>create();
168 writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
170 public void onSuccess(final CommitInfo result) {
171 LOG.debug("add-key-pair success. Input: {}", input);
172 rpcResult.set(RpcResultBuilder.success(new AddKeystoreEntryOutputBuilder().build()).build());
176 public void onFailure(final Throwable throwable) {
177 LOG.warn("add-key-pair failed. Input: {}", input, throwable);
178 rpcResult.setException(throwable);
180 }, MoreExecutors.directExecutor());
184 private String encryptString(final String plain) throws GeneralSecurityException {
185 return Base64.getEncoder().encodeToString(encryptionService.encrypt(plain.getBytes(StandardCharsets.UTF_8)));
189 ListenableFuture<RpcResult<AddTrustedCertificateOutput>> addTrustedCertificate(
190 final AddTrustedCertificateInput input) {
191 final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
193 for (TrustedCertificate certificate : input.nonnullTrustedCertificate().values()) {
194 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION,
195 KEYSTORE_IID.child(TrustedCertificate.class, certificate.key()), certificate);
198 final SettableFuture<RpcResult<AddTrustedCertificateOutput>> rpcResult = SettableFuture.create();
200 writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
202 public void onSuccess(final CommitInfo result) {
203 LOG.debug("add-trusted-certificate success. Input: {}", input);
204 rpcResult.set(RpcResultBuilder.success(new AddTrustedCertificateOutputBuilder().build()).build());
208 public void onFailure(final Throwable throwable) {
209 LOG.warn("add-trusted-certificate failed. Input: {}", input, throwable);
210 rpcResult.setException(throwable);
212 }, MoreExecutors.directExecutor());
217 private ListenableFuture<RpcResult<RemoveTrustedCertificateOutput>> removeTrustedCertificate(
218 final RemoveTrustedCertificateInput input) {
219 final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
221 for (final String name : input.getName()) {
222 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION,
223 KEYSTORE_IID.child(TrustedCertificate.class, new TrustedCertificateKey(name)));
226 final SettableFuture<RpcResult<RemoveTrustedCertificateOutput>> rpcResult = SettableFuture.create();
228 writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
230 public void onSuccess(final CommitInfo result) {
231 LOG.debug("remove-trusted-certificate success. Input: {}", input);
232 rpcResult.set(RpcResultBuilder.success(new RemoveTrustedCertificateOutputBuilder().build()).build());
236 public void onFailure(final Throwable throwable) {
237 LOG.warn("remove-trusted-certificate failed. Input: {}", input, throwable);
238 rpcResult.setException(throwable);
240 }, MoreExecutors.directExecutor());
246 ListenableFuture<RpcResult<AddPrivateKeyOutput>> addPrivateKey(final AddPrivateKeyInput input) {
247 final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
249 for (PrivateKey key: input.nonnullPrivateKey().values()) {
250 writeTransaction.merge(LogicalDatastoreType.CONFIGURATION,
251 KEYSTORE_IID.child(PrivateKey.class, key.key()), key);
254 final SettableFuture<RpcResult<AddPrivateKeyOutput>> rpcResult = SettableFuture.create();
256 writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
258 public void onSuccess(final CommitInfo result) {
259 LOG.debug("add-private-key success. Input: {}", input);
260 rpcResult.set(RpcResultBuilder.success(new AddPrivateKeyOutputBuilder().build()).build());
264 public void onFailure(final Throwable throwable) {
265 LOG.warn("add-private-key failed. Input: {}", input, throwable);
266 rpcResult.setException(throwable);
268 }, MoreExecutors.directExecutor());
273 private ListenableFuture<RpcResult<RemovePrivateKeyOutput>> removePrivateKey(final RemovePrivateKeyInput input) {
274 final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
276 for (final String name : input.getName()) {
277 writeTransaction.delete(LogicalDatastoreType.CONFIGURATION,
278 KEYSTORE_IID.child(PrivateKey.class, new PrivateKeyKey(name)));
281 final SettableFuture<RpcResult<RemovePrivateKeyOutput>> rpcResult = SettableFuture.create();
283 writeTransaction.commit().addCallback(new FutureCallback<CommitInfo>() {
285 public void onSuccess(final CommitInfo result) {
286 LOG.debug("remove-private-key success. Input: {}", input);
287 rpcResult.set(RpcResultBuilder.success(new RemovePrivateKeyOutputBuilder().build()).build());
291 public void onFailure(final Throwable throwable) {
292 LOG.warn("remove-private-key failed. Input: {}", input, throwable);
293 rpcResult.setException(throwable);
295 }, MoreExecutors.directExecutor());