Skip to content

Commit

Permalink
security: add AES encryption, DBAPI and Registry to store sensitive d…
Browse files Browse the repository at this point in the history
…ata and clean code from common vulnerability
  • Loading branch information
akardapolov committed Dec 27, 2022
1 parent 1dfc549 commit a7c8031
Show file tree
Hide file tree
Showing 27 changed files with 715 additions and 826 deletions.
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,24 @@ For Oracle standard edition and PostgreSQL, ASH Viewer emulate ASH, storing acti
mvn clean package -DskipTests=true
```

## Security
Passwords are stored in configuration file in encrypted form with secret key (computer name or hostname). So, when you run copied configuration on another host, you need to change password with a new secret key.
This is a minimal foolproof and for maximum protection it is necessary to store sensitive data using filesystem-level encryption or another way.

## Security
Encryption and Container settings provide security for database passwords (go to Other tab -> Security block)

### Encryption
Encryption setting has AES and PBE options
- **AES** - Advanced Encryption Standard (AES) with 256-bit key encryption, from the [Bouncy Castle provider](https://www.bouncycastle.org/), [FIPS](https://www.nist.gov/standardsgov/compliance-faqs-federal-information-processing-standards-fips#:~:text=are%20FIPS%20developed%3F-,What%20are%20Federal%20Information%20Processing%20Standards%20(FIPS)%3F,by%20the%20Secretary%20of%20Commerce.) compliant algorithm;
- **PBE** - Password based encryption (PBE) in PBEWithMD5AndDES mode with secret key (computer name or hostname). This option is weak and deprecated, please use AES when possible;

### Container
It's the way to store your encrypted data
- **DBAPI** - You sensitive data stored in Windows Data Protection API (DPAPI), maximum protected, Windows only;
- **Registry** - OS System registry storage using java preferences API - weak, but better than **Configuration**;
- **Configuration** - All data in local configuration file - weak, not recommended;

### Recommendations
- use AES encryption and Windows Data Protection API (DPAPI) whenever possible;
- do not use PBE copied configuration on another host, you need to change password with a new secret key (always do it for password leak prevention).

## Bugs and feature requests
Have a bug or a feature request? [Please open an issue](https://github.com/akardapolov/ASH-Viewer/issues)

Expand All @@ -77,6 +91,8 @@ Have a bug or a feature request? [Please open an issue](https://github.com/akard
- [Berkeley DB Java Edition](http://www.oracle.com/database/berkeley-db)
- [SwingLabs GUI toolkit by alexfromsun, kleopatra, rbair and other](https://en.wikipedia.org/wiki/SwingLabs)
- [Dagger 2 by Google](https://dagger.dev/)
- [AES cipher by Bouncy Castle](https://www.bouncycastle.org/)
- [Windows DPAPI Wrapper by @peter-gergely-horvath](https://github.com/peter-gergely-horvath/windpapi4j)

## License
[![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](http://perso.crans.org/besson/LICENSE.html)
Expand Down
4 changes: 2 additions & 2 deletions ashv/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
<source>11</source>
<target>11</target>
</configuration>
</plugin>

Expand Down
7 changes: 7 additions & 0 deletions ashv/src/main/java/config/profile/ConfigProfile.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package config.profile;

import core.manager.ConstantManager;
import gui.model.ContainerType;
import gui.model.EncryptionType;
import lombok.Data;

import java.util.List;
Expand All @@ -15,6 +17,11 @@ public class ConfigProfile {
private int rawRetainDays = ConstantManager.RETAIN_DAYS_MAX;
private int olapRetainDays = ConstantManager.RETAIN_DAYS_MAX;

private EncryptionType encryptionType;
private ContainerType containerType;
private String key;
private String iv;

private ConnProfile connProfile;
private List<SqlColProfile> sqlColProfileList;
}
81 changes: 81 additions & 0 deletions ashv/src/main/java/config/security/BCFipsConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package config.security;

import java.security.GeneralSecurityException;
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider;

/**
* This file contains API for key generation, encrypting and
* decrypting in CFB (Cipher Feedback) mode. It is similar to CBC while
* using a streaming block mode. However, padding is no longer required
* as the cipher generates a stream of "noise" which is XOR'd with the data
* to be encrypted.
*
* <a href="Based on source code from Indra Basak examples">https://github.com/indrabasak/bouncycastle-fips-examples</a>
*
* @author Indra Basak
* @since 11/18/2017
*/

@Slf4j
@Singleton
public class BCFipsConfig {

@Inject
public BCFipsConfig() {}

static {
Security.addProvider(new BouncyCastleFipsProvider());
}

/**
* Generates an AES key by providing a key size in bits.
*
* @return a symmetric key
* @throws GeneralSecurityException
*/
public SecretKey generateKey() throws GeneralSecurityException {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES", "BCFIPS");
keyGenerator.init(256);
return keyGenerator.generateKey();
}

/**
* Encrypts data in CFB (Cipher Feedback) mode.
*
* @param secretKey the secret key used for encryption
* @param data the plaintext to be encrypted
* @return array with initialization vector(IV) and encrypted data
* @throws GeneralSecurityException*
*/
public byte[][] cfbEncrypt(SecretKey secretKey, byte[] data)
throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding", "BCFIPS");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return new byte[][]{cipher.getIV(), cipher.doFinal(data)};
}

/**
* Decrypts the data in CFB (Cipher Feedback) mode.
*
* @param secretKey the secret key used for decryption
* @param iv initialization vector (IV)
* @param cipherText an encrypted ciphertext
* @return array with decrypted data
* @throws GeneralSecurityException
*/
public byte[] cfbDecrypt(SecretKey secretKey, byte[] iv,
byte[] cipherText) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding", "BCFIPS");
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
return cipher.doFinal(cipherText);
}

}
52 changes: 52 additions & 0 deletions ashv/src/main/java/config/security/ContainerConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package config.security;

import gui.model.RegistryKey;
import java.util.Base64;
import java.util.prefs.Preferences;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Singleton
public class ContainerConfig {
private final Preferences projectPreferences;
@Inject
public ContainerConfig() {
Preferences prefsRoot = Preferences.userRoot();
projectPreferences = prefsRoot.node("ASH-Viewer");
}

public String getRegistryValue(String profileName, RegistryKey registryKey) {
return projectPreferences.get(getKey(profileName, registryKey), "");
}

public void setRegistryValue(String profileName, RegistryKey registryKey, String value) {
projectPreferences.put(getKey(profileName, registryKey), value);
}

private String getKey(String profileName, RegistryKey registryKey) {
return profileName + "_" + registryKey;
}

public String convertSecretKeyToString(SecretKey secretKey) {
byte[] rawData = secretKey.getEncoded();
return Base64.getEncoder().encodeToString(rawData);
}

public SecretKey convertStringToSecretKey(String encodedKey) {
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
return new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
}

public String convertByteToString(byte[] rawData) {
return Base64.getEncoder().encodeToString(rawData);
}

public byte[] convertStringToByte(String encodedKey) {
return Base64.getDecoder().decode(encodedKey);
}

}
Loading

0 comments on commit a7c8031

Please sign in to comment.