]> git.basschouten.com Git - openhab-addons.git/blob
dbd0a0afb445c6b079dd0ccaf445c393f4a01582
[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.digitalstrom.internal.lib.serverconnection.impl;
14
15 import java.io.ByteArrayInputStream;
16 import java.io.File;
17 import java.io.FileInputStream;
18 import java.io.FileNotFoundException;
19 import java.io.FileWriter;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.net.HttpURLConnection;
23 import java.net.MalformedURLException;
24 import java.net.SocketTimeoutException;
25 import java.net.URL;
26 import java.nio.charset.StandardCharsets;
27 import java.security.KeyManagementException;
28 import java.security.NoSuchAlgorithmException;
29 import java.security.SecureRandom;
30 import java.security.Security;
31 import java.security.cert.CertificateEncodingException;
32 import java.security.cert.CertificateException;
33 import java.security.cert.CertificateFactory;
34 import java.security.cert.X509Certificate;
35 import java.util.Base64;
36
37 import javax.net.ssl.HostnameVerifier;
38 import javax.net.ssl.HttpsURLConnection;
39 import javax.net.ssl.SSLContext;
40 import javax.net.ssl.SSLHandshakeException;
41 import javax.net.ssl.SSLSession;
42 import javax.net.ssl.SSLSocketFactory;
43 import javax.net.ssl.TrustManager;
44 import javax.net.ssl.X509TrustManager;
45
46 import org.openhab.binding.digitalstrom.internal.lib.config.Config;
47 import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager;
48 import org.openhab.binding.digitalstrom.internal.lib.serverconnection.HttpTransport;
49 import org.openhab.binding.digitalstrom.internal.lib.serverconnection.simpledsrequestbuilder.constants.ParameterKeys;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 /**
54  * The {@link HttpTransportImpl} executes a request to the digitalSTROM-Server.
55  * <p>
56  * If a {@link Config} is given at the constructor. It sets the SSL-Certificate what is set in
57  * {@link Config#getCert()}. If there is no SSL-Certificate, but a path to an external SSL-Certificate file what is set
58  * in {@link Config#getTrustCertPath()} this will be set. If no SSL-Certificate is set in the {@link Config} it will be
59  * red out from the server and set in {@link Config#setCert(String)}.
60  *
61  * <p>
62  * If no {@link Config} is given the SSL-Certificate will be stored locally.
63  *
64  * <p>
65  * The method {@link #writePEMCertFile(String)} saves the SSL-Certificate in a file at the given path. If all
66  * SSL-Certificates shout be ignored the flag <i>exeptAllCerts</i> have to be true at the constructor
67  * </p>
68  * <p>
69  * If a {@link ConnectionManager} is given at the constructor, the session-token is not needed by requests and the
70  * {@link org.openhab.binding.digitalstrom.internal.lib.listener.ConnectionListener}, which is registered at the
71  * {@link ConnectionManager}, will be automatically informed about
72  * connection state changes through the {@link #execute(String, int, int)} method.
73  * </p>
74  *
75  * @author Michael Ochel - Initial contribution
76  * @author Matthias Siegele - Initial contribution
77  */
78 public class HttpTransportImpl implements HttpTransport {
79
80     private static final String LINE_SEPERATOR = System.getProperty("line.separator");
81     private static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----" + LINE_SEPERATOR;
82     private static final String END_CERT = LINE_SEPERATOR + "-----END CERTIFICATE-----" + LINE_SEPERATOR;
83
84     private final Logger logger = LoggerFactory.getLogger(HttpTransportImpl.class);
85     private static final short MAY_A_NEW_SESSION_TOKEN_IS_NEEDED = 1;
86
87     private String uri;
88
89     private int connectTimeout;
90     private int readTimeout;
91
92     private Config config;
93
94     private ConnectionManager connectionManager;
95
96     private String cert;
97     private SSLSocketFactory sslSocketFactory;
98     private final HostnameVerifier hostnameVerifier = new HostnameVerifier() {
99
100         @Override
101         public boolean verify(String arg0, SSLSession arg1) {
102             return arg0.equals(arg1.getPeerHost()) || arg0.contains("dss.local.");
103         }
104     };
105
106     /**
107      * Creates a new {@link HttpTransportImpl} with registration of the given {@link ConnectionManager} and set ignore
108      * all SSL-Certificates. The {@link Config} will be automatically added from the configurations of the given
109      * {@link ConnectionManager}.
110      *
111      * @param connectionManager to check connection, can be null
112      * @param exeptAllCerts (true = all will ignore)
113      */
114     public HttpTransportImpl(ConnectionManager connectionManager, boolean exeptAllCerts) {
115         this.connectionManager = connectionManager;
116         this.config = connectionManager.getConfig();
117         init(config.getHost(), config.getConnectionTimeout(), config.getReadTimeout(), exeptAllCerts);
118     }
119
120     /**
121      * Creates a new {@link HttpTransportImpl} with configurations of the given {@link Config} and set ignore all
122      * SSL-Certificates.
123      *
124      * @param config to get configurations, must not be null
125      * @param exeptAllCerts (true = all will ignore)
126      */
127     public HttpTransportImpl(Config config, boolean exeptAllCerts) {
128         this.config = config;
129         init(config.getHost(), config.getConnectionTimeout(), config.getReadTimeout(), exeptAllCerts);
130     }
131
132     /**
133      * Creates a new {@link HttpTransportImpl} with configurations of the given {@link Config}.
134      *
135      * @param config to get configurations, must not be null
136      */
137     public HttpTransportImpl(Config config) {
138         this.config = config;
139         init(config.getHost(), config.getConnectionTimeout(), config.getReadTimeout(), false);
140     }
141
142     /**
143      * Creates a new {@link HttpTransportImpl}.
144      *
145      * @param uri of the server, must not be null
146      */
147     public HttpTransportImpl(String uri) {
148         init(uri, Config.DEFAULT_CONNECTION_TIMEOUT, Config.DEFAULT_READ_TIMEOUT, false);
149     }
150
151     /**
152      * Creates a new {@link HttpTransportImpl} and set ignore all SSL-Certificates.
153      *
154      * @param uri of the server, must not be null
155      * @param exeptAllCerts (true = all will ignore)
156      */
157     public HttpTransportImpl(String uri, boolean exeptAllCerts) {
158         init(uri, Config.DEFAULT_CONNECTION_TIMEOUT, Config.DEFAULT_READ_TIMEOUT, exeptAllCerts);
159     }
160
161     /**
162      * Creates a new {@link HttpTransportImpl}.
163      *
164      * @param uri of the server, must not be null
165      * @param connectTimeout to set
166      * @param readTimeout to set
167      */
168     public HttpTransportImpl(String uri, int connectTimeout, int readTimeout) {
169         init(uri, connectTimeout, readTimeout, false);
170     }
171
172     /**
173      * Creates a new {@link HttpTransportImpl} and set ignore all SSL-Certificates..
174      *
175      * @param uri of the server, must not be null
176      * @param connectTimeout to set
177      * @param readTimeout to set
178      * @param exeptAllCerts (true = all will ignore)
179      */
180     public HttpTransportImpl(String uri, int connectTimeout, int readTimeout, boolean exeptAllCerts) {
181         init(uri, connectTimeout, readTimeout, exeptAllCerts);
182     }
183
184     private void init(String uri, int connectTimeout, int readTimeout, boolean exeptAllCerts) {
185         logger.debug("init HttpTransportImpl");
186         this.uri = fixURI(uri);
187         this.connectTimeout = connectTimeout;
188         this.readTimeout = readTimeout;
189         // Check SSL Certificate
190         if (exeptAllCerts) {
191             sslSocketFactory = generateSSLContextWhichAcceptAllSSLCertificats();
192         } else {
193             if (config != null) {
194                 cert = config.getCert();
195                 logger.debug("generate SSLcontext from config cert");
196                 if (cert != null && !cert.isBlank()) {
197                     sslSocketFactory = generateSSLContextFromPEMCertString(cert);
198                 } else {
199                     String trustCertPath = config.getTrustCertPath();
200                     if (trustCertPath != null && !trustCertPath.isBlank()) {
201                         logger.debug("generate SSLcontext from config cert path");
202                         cert = readPEMCertificateStringFromFile(trustCertPath);
203                         if (cert != null && !cert.isBlank()) {
204                             sslSocketFactory = generateSSLContextFromPEMCertString(cert);
205                         }
206                     } else {
207                         logger.debug("generate SSLcontext from server");
208                         cert = getPEMCertificateFromServer(this.uri);
209                         sslSocketFactory = generateSSLContextFromPEMCertString(cert);
210                         if (sslSocketFactory != null) {
211                             config.setCert(cert);
212                         }
213                     }
214                 }
215             } else {
216                 logger.debug("generate SSLcontext from server");
217                 cert = getPEMCertificateFromServer(this.uri);
218                 sslSocketFactory = generateSSLContextFromPEMCertString(cert);
219             }
220         }
221     }
222
223     private String fixURI(String uri) {
224         String fixedURI = uri;
225         if (!fixedURI.startsWith("https://")) {
226             fixedURI = "https://" + fixedURI;
227         }
228         if (fixedURI.split(":").length != 3) {
229             fixedURI = fixedURI + ":8080";
230         }
231         return fixedURI;
232     }
233
234     private String fixRequest(String request) {
235         return request.replace(" ", "");
236     }
237
238     @Override
239     public String execute(String request) {
240         return execute(request, this.connectTimeout, this.readTimeout);
241     }
242
243     private short loginCounter = 0;
244
245     @Override
246     public String execute(String request, int connectTimeout, int readTimeout) {
247         // NOTE: We will only show exceptions in the debug level, because they will be handled in the checkConnection()
248         // method and this changes the bridge state. If a command was send it fails than and a sensorJob will be
249         // execute the next time, by TimeOutExceptions. By other exceptions the checkConnection() method handles it in
250         // max 1 second.
251         String response = null;
252         HttpsURLConnection connection = null;
253         try {
254             String correctedRequest = checkSessionToken(request);
255             connection = getConnection(correctedRequest, connectTimeout, readTimeout);
256             if (connection != null) {
257                 connection.connect();
258                 final int responseCode = connection.getResponseCode();
259                 if (responseCode != HttpURLConnection.HTTP_FORBIDDEN) {
260                     if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) {
261                         response = new String(connection.getErrorStream().readAllBytes(), StandardCharsets.UTF_8);
262                     } else {
263                         response = new String(connection.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
264                     }
265                     if (response != null) {
266                         if (!response.contains("Authentication failed")) {
267                             if (loginCounter > 0) {
268                                 connectionManager.checkConnection(responseCode);
269                             }
270                             loginCounter = 0;
271                         } else {
272                             connectionManager.checkConnection(ConnectionManager.AUTHENTIFICATION_PROBLEM);
273                             loginCounter++;
274                         }
275                     }
276
277                 }
278                 connection.disconnect();
279                 if (response == null && connectionManager != null
280                         && loginCounter <= MAY_A_NEW_SESSION_TOKEN_IS_NEEDED) {
281                     if (responseCode == HttpURLConnection.HTTP_FORBIDDEN) {
282                         execute(addSessionToken(correctedRequest, connectionManager.getNewSessionToken()),
283                                 connectTimeout, readTimeout);
284                         loginCounter++;
285                     } else {
286                         connectionManager.checkConnection(responseCode);
287                         loginCounter++;
288                         return null;
289                     }
290                 }
291                 return response;
292             }
293         } catch (SocketTimeoutException e) {
294             informConnectionManager(ConnectionManager.SOCKET_TIMEOUT_EXCEPTION);
295         } catch (java.net.ConnectException e) {
296             informConnectionManager(ConnectionManager.CONNECTION_EXCEPTION);
297         } catch (MalformedURLException e) {
298             informConnectionManager(ConnectionManager.MALFORMED_URL_EXCEPTION);
299         } catch (java.net.UnknownHostException e) {
300             informConnectionManager(ConnectionManager.UNKNOWN_HOST_EXCEPTION);
301         } catch (SSLHandshakeException e) {
302             informConnectionManager(ConnectionManager.SSL_HANDSHAKE_EXCEPTION);
303         } catch (IOException e) {
304             logger.error("An IOException occurred: ", e);
305             informConnectionManager(ConnectionManager.GENERAL_EXCEPTION);
306         } finally {
307             if (connection != null) {
308                 connection.disconnect();
309             }
310         }
311         return null;
312     }
313
314     private boolean informConnectionManager(int code) {
315         if (connectionManager != null && loginCounter < MAY_A_NEW_SESSION_TOKEN_IS_NEEDED) {
316             connectionManager.checkConnection(code);
317             return true;
318         }
319         return false;
320     }
321
322     private String checkSessionToken(String request) {
323         if (checkNeededSessionToken(request)) {
324             if (connectionManager != null) {
325                 String sessionToken = connectionManager.getSessionToken();
326                 if (sessionToken == null) {
327                     return addSessionToken(request, connectionManager.getNewSessionToken());
328                 }
329                 return addSessionToken(request, sessionToken);
330             }
331         }
332         return request;
333     }
334
335     private boolean checkNeededSessionToken(String request) {
336         String requestFirstPart = request;
337         int indexOfSeparator = request.indexOf("?");
338         if (indexOfSeparator >= 0) {
339             requestFirstPart = request.substring(0, request.indexOf("?"));
340         }
341         String functionName = requestFirstPart.substring(requestFirstPart.lastIndexOf("/") + 1);
342         return !DsAPIImpl.METHODS_MUST_NOT_BE_LOGGED_IN.contains(functionName);
343     }
344
345     private String addSessionToken(String request, String sessionToken) {
346         String correctedRequest = request;
347         if (!correctedRequest.contains(ParameterKeys.TOKEN)) {
348             if (correctedRequest.contains("?")) {
349                 correctedRequest = correctedRequest + "&" + ParameterKeys.TOKEN + "=" + sessionToken;
350             } else {
351                 correctedRequest = correctedRequest + "?" + ParameterKeys.TOKEN + "=" + sessionToken;
352             }
353         } else {
354             String strippedRequest = correctedRequest
355                     .substring(correctedRequest.indexOf(ParameterKeys.TOKEN + "=") + ParameterKeys.TOKEN.length() + 1);
356             int indexOfSeparator = strippedRequest.indexOf("&");
357             if (indexOfSeparator >= 0) {
358                 strippedRequest = strippedRequest.substring(0, indexOfSeparator);
359             }
360             correctedRequest = correctedRequest.replaceFirst(strippedRequest, sessionToken);
361         }
362         return correctedRequest;
363     }
364
365     private HttpsURLConnection getConnection(String request, int connectTimeout, int readTimeout) throws IOException {
366         String correctedRequest = request;
367         if (correctedRequest != null && !correctedRequest.isBlank()) {
368             correctedRequest = fixRequest(correctedRequest);
369             URL url = new URL(this.uri + correctedRequest);
370             HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
371             if (connection != null) {
372                 connection.setConnectTimeout(connectTimeout);
373                 connection.setReadTimeout(readTimeout);
374                 if (sslSocketFactory != null) {
375                     connection.setSSLSocketFactory(sslSocketFactory);
376                 }
377                 if (hostnameVerifier != null) {
378                     connection.setHostnameVerifier(hostnameVerifier);
379                 }
380             }
381             return connection;
382         }
383         return null;
384     }
385
386     @Override
387     public int checkConnection(String testRequest) {
388         try {
389             HttpsURLConnection connection = getConnection(testRequest, connectTimeout, readTimeout);
390             if (connection != null) {
391                 connection.connect();
392                 if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
393                     if (new String(connection.getInputStream().readAllBytes(), StandardCharsets.UTF_8)
394                             .contains("Authentication failed")) {
395                         return ConnectionManager.AUTHENTIFICATION_PROBLEM;
396                     }
397                 }
398                 connection.disconnect();
399                 return connection.getResponseCode();
400             } else {
401                 return ConnectionManager.GENERAL_EXCEPTION;
402             }
403         } catch (SocketTimeoutException e) {
404             return ConnectionManager.SOCKET_TIMEOUT_EXCEPTION;
405         } catch (java.net.ConnectException e) {
406             return ConnectionManager.CONNECTION_EXCEPTION;
407         } catch (MalformedURLException e) {
408             return ConnectionManager.MALFORMED_URL_EXCEPTION;
409         } catch (java.net.UnknownHostException e) {
410             return ConnectionManager.UNKNOWN_HOST_EXCEPTION;
411         } catch (IOException e) {
412             return ConnectionManager.GENERAL_EXCEPTION;
413         }
414     }
415
416     @Override
417     public int getSensordataConnectionTimeout() {
418         return config != null ? config.getSensordataConnectionTimeout() : Config.DEFAULT_SENSORDATA_CONNECTION_TIMEOUT;
419     }
420
421     @Override
422     public int getSensordataReadTimeout() {
423         return config != null ? config.getSensordataReadTimeout() : Config.DEFAULT_SENSORDATA_READ_TIMEOUT;
424     }
425
426     private String readPEMCertificateStringFromFile(String path) {
427         if (path == null || path.isBlank()) {
428             logger.error("Path is empty.");
429         } else {
430             File dssCert = new File(path);
431             if (dssCert.exists()) {
432                 if (path.endsWith(".crt")) {
433                     try (InputStream certInputStream = new FileInputStream(dssCert)) {
434                         String cert = new String(certInputStream.readAllBytes(), StandardCharsets.UTF_8);
435                         if (cert.startsWith(BEGIN_CERT)) {
436                             return cert;
437                         } else {
438                             logger.error("File is not a PEM certificate file. PEM-Certificates starts with: {}",
439                                     BEGIN_CERT);
440                         }
441                     } catch (FileNotFoundException e) {
442                         logger.error("Can't find a certificate file at the path: {}\nPlease check the path!", path);
443                     } catch (IOException e) {
444                         logger.error("An IOException occurred: ", e);
445                     }
446                 } else {
447                     logger.error("File is not a certificate (.crt) file.");
448                 }
449             } else {
450                 logger.error("File not found");
451             }
452         }
453         return null;
454     }
455
456     @Override
457     public String writePEMCertFile(String path) {
458         String correctedPath = path == null ? "" : path.trim();
459         File certFilePath;
460         if (!correctedPath.isBlank()) {
461             certFilePath = new File(correctedPath);
462             boolean pathExists = certFilePath.exists();
463             if (!pathExists) {
464                 pathExists = certFilePath.mkdirs();
465             }
466             if (pathExists && !correctedPath.endsWith("/")) {
467                 correctedPath = correctedPath + "/";
468             }
469         }
470         InputStream certInputStream = new ByteArrayInputStream(cert.getBytes(StandardCharsets.UTF_8));
471         X509Certificate trustedCert;
472         try {
473             trustedCert = (X509Certificate) CertificateFactory.getInstance("X.509")
474                     .generateCertificate(certInputStream);
475
476             certFilePath = new File(
477                     correctedPath + trustedCert.getSubjectDN().getName().split(",")[0].substring(2) + ".crt");
478             if (!certFilePath.exists()) {
479                 certFilePath.createNewFile();
480                 FileWriter writer = new FileWriter(certFilePath, true);
481                 writer.write(cert);
482                 writer.flush();
483                 writer.close();
484                 return certFilePath.getAbsolutePath();
485             } else {
486                 logger.error("File allready exists!");
487             }
488         } catch (IOException e) {
489             logger.error("An IOException occurred: ", e);
490         } catch (CertificateException e1) {
491             logger.error("A CertificateException occurred: ", e1);
492         }
493         return null;
494     }
495
496     private SSLSocketFactory generateSSLContextFromPEMCertString(String pemCert) {
497         if (pemCert != null && !pemCert.isBlank() && pemCert.startsWith(BEGIN_CERT)) {
498             try {
499                 InputStream certInputStream = new ByteArrayInputStream(pemCert.getBytes(StandardCharsets.UTF_8));
500                 final X509Certificate trustedCert = (X509Certificate) CertificateFactory.getInstance("X.509")
501                         .generateCertificate(certInputStream);
502
503                 final TrustManager[] trustManager = new TrustManager[] { new X509TrustManager() {
504
505                     @Override
506                     public java.security.cert.X509Certificate[] getAcceptedIssuers() {
507                         return null;
508                     }
509
510                     @Override
511                     public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
512                             throws CertificateException {
513                         if (!certs[0].equals(trustedCert)) {
514                             throw new CertificateException();
515                         }
516                     }
517
518                     @Override
519                     public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
520                             throws CertificateException {
521                         if (!certs[0].equals(trustedCert)) {
522                             throw new CertificateException();
523                         }
524                     }
525                 } };
526
527                 SSLContext sslContext = SSLContext.getInstance("SSL");
528                 sslContext.init(null, trustManager, new java.security.SecureRandom());
529                 return sslContext.getSocketFactory();
530             } catch (NoSuchAlgorithmException e) {
531                 logger.error("A NoSuchAlgorithmException occurred: ", e);
532             } catch (KeyManagementException e) {
533                 logger.error("A KeyManagementException occurred: ", e);
534             } catch (CertificateException e) {
535                 logger.error("A CertificateException occurred: ", e);
536             }
537         } else {
538             logger.error("Cert is empty");
539         }
540         return null;
541     }
542
543     private String getPEMCertificateFromServer(String host) {
544         HttpsURLConnection connection = null;
545         try {
546             URL url = new URL(host);
547
548             connection = (HttpsURLConnection) url.openConnection();
549             connection.setHostnameVerifier(hostnameVerifier);
550             connection.setSSLSocketFactory(generateSSLContextWhichAcceptAllSSLCertificats());
551             connection.connect();
552
553             java.security.cert.Certificate[] cert = connection.getServerCertificates();
554             connection.disconnect();
555
556             byte[] by = ((X509Certificate) cert[0]).getEncoded();
557             if (by.length != 0) {
558                 return BEGIN_CERT + Base64.getEncoder().encodeToString(by) + END_CERT;
559             }
560         } catch (MalformedURLException e) {
561             if (!informConnectionManager(ConnectionManager.MALFORMED_URL_EXCEPTION)) {
562                 logger.error("A MalformedURLException occurred: ", e);
563             }
564         } catch (IOException e) {
565             short code = ConnectionManager.GENERAL_EXCEPTION;
566             if (e instanceof java.net.ConnectException) {
567                 code = ConnectionManager.CONNECTION_EXCEPTION;
568             } else if (e instanceof java.net.UnknownHostException) {
569                 code = ConnectionManager.UNKNOWN_HOST_EXCEPTION;
570             }
571             if (!informConnectionManager(code) || code == -1) {
572                 logger.error("An IOException occurred: ", e);
573             }
574         } catch (CertificateEncodingException e) {
575             logger.error("A CertificateEncodingException occurred: ", e);
576         } finally {
577             if (connection != null) {
578                 connection.disconnect();
579             }
580         }
581         return null;
582     }
583
584     private SSLSocketFactory generateSSLContextWhichAcceptAllSSLCertificats() {
585         Security.addProvider(Security.getProvider("SunJCE"));
586         TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
587
588             @Override
589             public java.security.cert.X509Certificate[] getAcceptedIssuers() {
590                 return null;
591             }
592
593             @Override
594             public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
595             }
596
597             @Override
598             public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
599             }
600         } };
601
602         try {
603             SSLContext sslContext = SSLContext.getInstance("SSL");
604
605             sslContext.init(null, trustAllCerts, new SecureRandom());
606
607             return sslContext.getSocketFactory();
608         } catch (KeyManagementException e) {
609             logger.error("A KeyManagementException occurred", e);
610         } catch (NoSuchAlgorithmException e) {
611             logger.error("A NoSuchAlgorithmException occurred", e);
612         }
613         return null;
614     }
615 }