2 * ============LICENSE_START======================================================================
3 * Copyright (C) 2018-2023 Nordix Foundation. All rights reserved.
4 * Copyright (C) 2020-2021 Nokia. All rights reserved.
5 * ===============================================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7 * in compliance with the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software distributed under the License
12 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13 * or implied. See the License for the specific language governing permissions and limitations under
15 * ============LICENSE_END========================================================================
18 package org.oran.datafile.ftp;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStream;
25 import java.nio.file.Path;
26 import java.security.GeneralSecurityException;
27 import java.security.KeyStore;
28 import java.security.KeyStoreException;
29 import java.security.NoSuchAlgorithmException;
30 import java.security.UnrecoverableKeyException;
31 import java.security.cert.CertificateException;
33 import javax.net.ssl.KeyManager;
34 import javax.net.ssl.KeyManagerFactory;
35 import javax.net.ssl.TrustManager;
36 import javax.net.ssl.TrustManagerFactory;
38 import org.apache.commons.net.ftp.FTP;
39 import org.apache.commons.net.ftp.FTPReply;
40 import org.apache.commons.net.ftp.FTPSClient;
41 import org.oran.datafile.commons.FileCollectClient;
42 import org.oran.datafile.commons.FileServerData;
43 import org.oran.datafile.commons.SecurityUtil;
44 import org.oran.datafile.exceptions.DatafileTaskException;
45 import org.oran.datafile.exceptions.NonRetryableDatafileTaskException;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48 import org.springframework.core.io.FileSystemResource;
51 * Gets file from PNF with FTPS protocol.
53 public class FtpesClient implements FileCollectClient {
54 private static final Logger logger = LoggerFactory.getLogger(FtpesClient.class);
56 private static final int DEFAULT_PORT = 21;
58 FTPSClient realFtpsClient = new FTPSClient();
59 private final FileServerData fileServerData;
60 private static TrustManager theTrustManager = null;
61 private static KeyManager theKeyManager = null;
63 private final Path keyCertPath;
64 private final String keyCertPasswordPath;
65 private final Path trustedCaPath;
66 private final String trustedCaPasswordPath;
71 * @param fileServerData info needed to connect to the PNF.
72 * @param keyCertPath path to DFC's key cert.
73 * @param keyCertPasswordPath path of file containing password for DFC's key
75 * @param trustedCaPath path to the PNF's trusted keystore.
76 * @param trustedCaPasswordPath path of file containing password for the PNF's
79 public FtpesClient(FileServerData fileServerData, Path keyCertPath, String keyCertPasswordPath, Path trustedCaPath,
80 String trustedCaPasswordPath) {
81 this.fileServerData = fileServerData;
82 this.keyCertPath = keyCertPath;
83 this.keyCertPasswordPath = keyCertPasswordPath;
84 this.trustedCaPath = trustedCaPath;
85 this.trustedCaPasswordPath = trustedCaPasswordPath;
89 public void open() throws DatafileTaskException {
91 realFtpsClient.setNeedClientAuth(trustedCaPath != null);
92 realFtpsClient.setKeyManager(getKeyManager(keyCertPath, keyCertPasswordPath));
93 realFtpsClient.setTrustManager(getTrustManager(trustedCaPath, trustedCaPasswordPath));
95 } catch (DatafileTaskException e) {
97 } catch (Exception e) {
98 throw new DatafileTaskException("Could not open connection: " + e, e);
103 public void close() {
104 logger.trace("starting to closeDownConnection");
105 if (realFtpsClient.isConnected()) {
107 boolean logOut = realFtpsClient.logout();
108 logger.trace("logOut: {}", logOut);
109 } catch (Exception e) {
110 logger.trace("Unable to logout connection.", e);
113 realFtpsClient.disconnect();
114 logger.trace("disconnected!");
115 } catch (Exception e) {
116 logger.trace("Unable to disconnect connection.", e);
122 public void collectFile(String remoteFileName, Path localFileName) throws DatafileTaskException {
123 logger.trace("collectFile called");
125 try (OutputStream output = createOutputStream(localFileName)) {
126 logger.trace("begin to retrieve from xNF.");
127 if (!realFtpsClient.retrieveFile(remoteFileName, output)) {
128 throw new NonRetryableDatafileTaskException(
129 "Could not retrieve file. No retry attempts will be done, file :" + remoteFileName);
131 } catch (IOException e) {
132 throw new DatafileTaskException("Could not fetch file: " + e, e);
134 logger.trace("collectFile fetched: {}", localFileName);
137 private static int getPort(Integer port) {
138 return port != null ? port : DEFAULT_PORT;
141 private void setUpConnection() throws DatafileTaskException, IOException {
143 realFtpsClient.connect(fileServerData.serverAddress, getPort(fileServerData.port));
144 logger.trace("after ftp connect");
146 if (!realFtpsClient.login(fileServerData.userId, fileServerData.password)) {
147 throw new DatafileTaskException("Unable to log in to xNF. " + fileServerData.serverAddress);
150 if (FTPReply.isPositiveCompletion(realFtpsClient.getReplyCode())) {
151 realFtpsClient.enterLocalPassiveMode();
152 realFtpsClient.setFileType(FTP.BINARY_FILE_TYPE);
153 // Set protection buffer size
154 realFtpsClient.execPBSZ(0);
155 // Set data channel protection to private
156 realFtpsClient.execPROT("P");
157 realFtpsClient.setBufferSize(1024 * 1024);
159 throw new DatafileTaskException("Unable to connect to xNF. " + fileServerData.serverAddress
160 + " xNF reply code: " + realFtpsClient.getReplyCode());
163 logger.trace("setUpConnection successfully!");
166 private TrustManager createTrustManager(Path trustedCaPath, String trustedCaPassword)
167 throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
168 logger.trace("Creating trust manager from file: {}", trustedCaPath);
169 try (InputStream fis = createInputStream(trustedCaPath)) {
170 KeyStore keyStore = KeyStore.getInstance("JKS");
171 keyStore.load(fis, trustedCaPassword.toCharArray());
172 TrustManagerFactory factory = TrustManagerFactory.getInstance("SunX509");
173 factory.init(keyStore);
174 return factory.getTrustManagers()[0];
178 protected InputStream createInputStream(Path localFileName) throws IOException {
179 FileSystemResource realResource = new FileSystemResource(localFileName);
180 return realResource.getInputStream();
183 protected OutputStream createOutputStream(Path localFileName) throws IOException, DatafileTaskException {
184 File localFile = localFileName.toFile();
185 if (!localFile.createNewFile()) {
186 logger.debug("Local file {} already created", localFileName);
187 throw new NonRetryableDatafileTaskException("Local file already created: " + localFileName);
189 OutputStream output = new FileOutputStream(localFile);
190 logger.trace("File {} opened xNF", localFileName);
194 protected TrustManager getTrustManager(Path trustedCaPath, String trustedCaPasswordPath)
195 throws KeyStoreException, NoSuchAlgorithmException, IOException, CertificateException {
196 synchronized (FtpesClient.class) {
197 if (theTrustManager == null && trustedCaPath != null) {
198 String trustedCaPassword = SecurityUtil.getTruststorePasswordFromFile(trustedCaPasswordPath);
199 theTrustManager = createTrustManager(trustedCaPath, trustedCaPassword);
201 return theTrustManager;
205 protected KeyManager getKeyManager(Path keyCertPath, String keyCertPasswordPath)
206 throws IOException, GeneralSecurityException {
208 synchronized (FtpesClient.class) {
209 if (theKeyManager == null) {
210 String keyCertPassword = SecurityUtil.getKeystorePasswordFromFile(keyCertPasswordPath);
211 theKeyManager = createKeyManager(keyCertPath, keyCertPassword);
213 return theKeyManager;
217 private KeyManager createKeyManager(Path keyCertPath, String keyCertPassword) throws IOException, KeyStoreException,
218 NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
219 logger.trace("Creating key manager from file: {}", keyCertPath);
220 try (InputStream fis = createInputStream(keyCertPath)) {
221 KeyStore keyStore = KeyStore.getInstance("JKS");
222 keyStore.load(fis, keyCertPassword.toCharArray());
223 KeyManagerFactory factory = KeyManagerFactory.getInstance("SunX509");
224 factory.init(keyStore, keyCertPassword.toCharArray());
225 return factory.getKeyManagers()[0];