Skip to content

Commit

Permalink
Merge pull request apache#4405 from hansva/main
Browse files Browse the repository at this point in the history
  • Loading branch information
hansva authored Oct 9, 2024
2 parents 02ad78a + 9f47929 commit 748cd46
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,15 @@ public void checkClientTrusted(X509Certificate[] chain, String authType)
return sslContext;
}

/**
* This SSLContext is used when a user chooses to TrustAll and ignore SSL and Certificate
* validation
*
* @return SSLContext
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
@SuppressWarnings({"java:S4830", "java:S4423"})
public static SSLContext getTrustAllSslContext()
throws NoSuchAlgorithmException, KeyManagementException {
TrustManager[] trustAllCerts =
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/java/org/apache/hop/core/xml/XmlHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,8 @@ public static TransformerFactory createSecureTransformerFactory() {
try {
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
} catch (IllegalArgumentException e) {
transformerFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
} catch (IllegalArgumentException | TransformerException e) {
// Ignore this: the library doesn't support these features.
// We don't need to disable them.
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
*/
public class ServerConnectionManager {

private static final String SSL = "SSL";
private static final String SSL = "TLSv1.2";
private static final String KEYSTORE_SYSTEM_PROPERTY = "javax.net.ssl.keyStore";

private static ServerConnectionManager serverConnectionManager;
Expand All @@ -50,6 +50,7 @@ private ServerConnectionManager() {
new SecureRandom());
SSLContext.setDefault(context);
} catch (Exception ignored) {
// Ignore Exception
}
}
manager = new PoolingHttpClientConnectionManager();
Expand Down Expand Up @@ -78,7 +79,16 @@ public void shutdown() {

private static X509TrustManager getDefaultTrustManager() {
return new X509TrustManager() {
/**
* Hop Allows self-signed certificates for Hop Server, disable the check for now This is only
* used for communication to Hop Server
*
* @param certs the peer certificate chain
* @param param the authentication type based on the client certificate
* @throws CertificateException
*/
@Override
@SuppressWarnings("java:S4830")
public void checkClientTrusted(X509Certificate[] certs, String param)
throws CertificateException {
// Do nothing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ under the License.

= AES Two way password encoder

WARNING: The AES prefix is deprecated since version 2.11, use the AES2 prefix this prefix offers better cryptographic strength. Moving from AES to AES2 prefix requires you te re-generate the password strings

== Description

The AES two-way password encoder plugin allows you to encrypt and decrypt passwords using a provided key string.
Expand All @@ -28,12 +30,12 @@ The AES two-way password encoder plugin allows you to encrypt and decrypt passwo

The configuration of this encoder is for the whole Hop environment and can as such only be done using system properties. These properties are listed below and can also be specified as system properties with hop-run:

* *`HOP_PASSWORD_ENCODER_PLUGIN`*: set this to the ID of this plugin: AES
* *`HOP_PASSWORD_ENCODER_PLUGIN`*: set this to the ID of this plugin: AES2
* *`HOP_AES_ENCODER_KEY`*: Specify the key to use to encode or decode passwords

== Noteworthy

Please note that passwords are encoded with prefix ```AES ``` which is AES plus a space.
Please note that passwords are encoded with prefix ```AES2 ``` which is AES2 plus a space.
This means that it's different from standard, Hop encoded, passwords which have prefix ```Encrypted ``` which is Encrypted plus a space.
The consequence of this is that you need to encode passwords either one way or another.
Mixing password encoding is not supported.
Expand All @@ -58,12 +60,10 @@ You can test your setup by setting the variables in your environment and then by

[source,bash]
----
$:~/hop$ export HOP_PASSWORD_ENCODER_PLUGIN=AES
$:~/hop$ export HOP_AES_ENCODER_KEY=ddsfsdfsfsdf
$:~/hop$ sh hop-encrypt.sh -hop MyPassword222
AES kb/8yIPqClL/0+/e+gsVcw==
$:~/hop$ export HOP_AES_ENCODER_KEY=abcd
$:~/hop$ sh hop-encrypt.sh -hop MyPassword222
AES s7JDgDBqQWMh1E/RWgGaUA==
export HOP_PASSWORD_ENCODER_PLUGIN=AES2
export HOP_AES_ENCODER_KEY=ddsfsdfsfsdf
sh hop-encrypt.sh -hop MyPassword222
AES2 696N5ATiXqU0AxdkLpN+UT67Ud5P6TMkq7OGSRc=
----
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;

/** TrustStore manager to allow all connections */
@SuppressWarnings("java:S4830")
public class TrustAlwaysManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] cert, String authType) {
return;
// Do Nothing
}

@Override
public void checkServerTrusted(X509Certificate[] cert, String authType) {
return;
// Do Nothing
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.apache.hop.passwords.aes;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.lang.StringUtils;
import org.apache.hop.core.encryption.ITwoWayPasswordEncoder;
import org.apache.hop.core.encryption.TwoWayPasswordEncoderPlugin;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.util.StringUtil;
import org.apache.hop.core.variables.Variables;

/**
* We expect a few variables to be set for this plugin to be picked up: 1.
* HOP_PASSWORD_ENCODER_PLUGIN set to the ID of this plugin:"AES" 2. HOP_AES_ENCODER_KEY set to the
* key of your choice.
*/
@TwoWayPasswordEncoderPlugin(
id = "AES2",
name = "AES2 Password encoder",
description = "Allows for 128/192/256 bit password encryption of passwords in Hop")
public class Aes2TwoWayPasswordEncoder implements ITwoWayPasswordEncoder {

public static final String VARIABLE_HOP_AES_ENCODER_KEY = "HOP_AES_ENCODER_KEY";
public static final String AES_PREFIX = "AES2 ";
public static final String AES_ALGORITHM = "AES/GCM/NoPadding";

private Cipher encryptCipher;
private Cipher decryptCipher;

@Override
public void init() throws HopException {

try {
String aesKey = System.getProperty(VARIABLE_HOP_AES_ENCODER_KEY, null);
if (StringUtils.isEmpty(aesKey)) {
noKeySpecified();
}
String realAesKey = Variables.getADefaultVariableSpace().resolve(aesKey);
if (StringUtils.isEmpty(realAesKey)) {
noKeySpecified();
}
byte[] key = realAesKey.getBytes(StandardCharsets.UTF_8);
MessageDigest messageDigest = MessageDigest.getInstance("SHA-512");
byte[] digestKey = messageDigest.digest(key);
byte[] copiedKey = Arrays.copyOf(digestKey, 16);
SecretKeySpec secretKeySpec = new SecretKeySpec(copiedKey, "AES");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, copiedKey);

// Create the cyphers that will do the encoding/decoding below...
//
encryptCipher = Cipher.getInstance(AES_ALGORITHM);
encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, parameterSpec);

decryptCipher = Cipher.getInstance(AES_ALGORITHM);
decryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, parameterSpec);
} catch (Exception e) {
throw new HopException("Error initializing AES password encoder plugin", e);
}
}

private void noKeySpecified() throws HopException {
throw new HopException(
"Please specify a key to encrypt/decrypt with by setting variable "
+ VARIABLE_HOP_AES_ENCODER_KEY
+ " in the system properties");
}

@Override
public String encode(String password) {
return encode(password, true);
}

@Override
public String encode(String password, boolean includePrefix) {
if (StringUtils.isEmpty(password)) {
return password;
}
try {
if (includePrefix) {
return encryptPasswordIfNotUsingVariablesInternal(password);
} else {
return encodeInternal(password);
}
} catch (Exception e) {
throw new RuntimeException("Error encoding password using AES", e);
}
}

private String encodeInternal(String password) {
if (StringUtils.isEmpty(password)) {
return password;
}
try {
return Base64.getEncoder()
.encodeToString(encryptCipher.doFinal(password.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
throw new RuntimeException("Error encoding password using AES", e);
}
}

@Override
public String decode(String encodedPassword, boolean optionallyEncrypted) {
if (StringUtils.isEmpty(encodedPassword)) {
return encodedPassword;
}
if (optionallyEncrypted) {
if (encodedPassword.startsWith(AES_PREFIX)) {
encodedPassword = encodedPassword.substring(AES_PREFIX.length());
return decodeOnly(encodedPassword);
} else {
return encodedPassword;
}
} else {
return decodeOnly(encodedPassword);
}
}

@Override
public String decode(String encodedPassword) {
if (StringUtils.isEmpty(encodedPassword)) {
return encodedPassword;
}
if (encodedPassword.startsWith(AES_PREFIX)) {
encodedPassword = encodedPassword.substring(AES_PREFIX.length());
}

return decodeOnly(encodedPassword);
}

/**
* Encrypt the password, but only if the password doesn't contain any variables.
*
* @param password The password to encrypt
* @return The encrypted password or the
*/
protected final String encryptPasswordIfNotUsingVariablesInternal(String password) {
String encryptedPassword = "";
List<String> varList = new ArrayList<>();
StringUtil.getUsedVariables(password, varList, true);
if (varList.isEmpty()) {
encryptedPassword = AES_PREFIX + encodeInternal(password);
} else {
encryptedPassword = password;
}

return encryptedPassword;
}

private String decodeOnly(String encodedPassword) {
try {
return new String(decryptCipher.doFinal(Base64.getDecoder().decode(encodedPassword)));
} catch (Exception e) {
throw new RuntimeException("Error decoding password using AES", e);
}
}

@Override
public String[] getPrefixes() {
return new String[] {AES_PREFIX};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@
import org.apache.hop.core.variables.Variables;

/**
* We expect a few variables to be set for this plugin to be picked up: 1.
* HOP_PASSWORD_ENCODER_PLUGIN set to the ID of this plugin:"AES" 2. HOP_AES_ENCODER_KEY set to the
* key of your choice.
* @deprecated We expect a few variables to be set for this plugin to be picked up: 1.
* HOP_PASSWORD_ENCODER_PLUGIN set to the ID of this plugin:"AES" 2. HOP_AES_ENCODER_KEY set to
* the key of your choice.
*/
@TwoWayPasswordEncoderPlugin(
id = "AES",
name = "AES Password encoder",
description = "Allows for 128/192/256 bit password encryption of passwords in Hop")
name = "AES Password encoder (Deprecated)",
description = "Allows for 128/192/256 bit password encryption of passwords in Hop (Deprecated)")
@Deprecated(since = "2.11")
@SuppressWarnings({"java:S5542", "java:S4790"})
public class AesTwoWayPasswordEncoder implements ITwoWayPasswordEncoder {

public static final String VARIABLE_HOP_AES_ENCODER_KEY = "HOP_AES_ENCODER_KEY";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ protected String getConnectionPrefix() {
*
* @return the display name
*/
// Suppress the LDAP connection warning
@SuppressWarnings("java:S4433")
public static String getName() {
return NAME;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ MultivaluedHashMap createMultivalueMap(String paramName, String paramValue) {
return queryParams;
}

/**
* Perform the rest call Ignore Sonar SSL warning, SSL can be disabled by a user action
*
* @param rowData
* @return
* @throws HopException
*/
@SuppressWarnings("java:S5527")
protected Object[] callRest(Object[] rowData) throws HopException {
// get dynamic url ?
if (meta.isUrlInField()) {
Expand All @@ -104,10 +112,16 @@ protected Object[] callRest(Object[] rowData) throws HopException {
clientBuilder
.withConfig(data.config)
.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
if (meta.isIgnoreSsl() || !Utils.isEmpty(data.trustStoreFile)) {

if (!Utils.isEmpty(data.trustStoreFile)) {
clientBuilder.sslContext(data.sslContext);
}

// Ignore SSL is selected disable hostname verifier
if (meta.isIgnoreSsl()) {
clientBuilder.hostnameVerifier((s1, s2) -> true);
}

client = clientBuilder.build();
if (data.basicAuthentication != null) {
client.register(data.basicAuthentication);
Expand Down
Loading

0 comments on commit 748cd46

Please sign in to comment.