2
\$\begingroup\$

I have a program that makes a call to an SQL database. I cannot use SSH to connect to it (as far as I am aware) or any other more secure method, so Java's SQL connector was used. As the connection requires a username/password, and I want to store the user/pass for ease of making the connection. I knew I was not going to store the password in plain text, but I couldn't find a method of doing so in a secure manner in a platform-independent way in core Java, so I rolled my own (not advised in crypto, I know).

Below is the code that encrypts a message of any length with a key of any length.

private static String xor(String message, String key){
    if(message.isEmpty())
        return "";
    byte[] xorBlob=new byte[message.length()];
    long seed=0;
    for(char c: key.toCharArray())
        seed+=c;
    new Random(seed).nextBytes(xorBlob);
    char[] mArr=message.toCharArray();
    for(int i=0;i<mArr.length;i++)
        mArr[i]=(char)(mArr[i]^xorBlob[i]);
    return new String(mArr);
}

To put the above code into words: It sums up all the char values of the given key into an int. That int is then used as the seed for the Random function, which then spits out an array of bytes the length of the message. The message and that byte array are then xor'd together.

It's rudimentary, but I figured for opt-in local storage of a password for the current user, it should suffice.

Is this a good method of doing so? Is there a way I can improve this code, for either more security or just better code to more fit industry standards?

EDIT: Here's the code that calls the above function:

FileWriter writer=new FileWriter(config);
writer.write(String.format("username=%s%n", username));
writer.write(String.format("password=%s%n", xor(password, username)));
\$\endgroup\$
8
  • 1
    \$\begingroup\$ To get a better review you post the code that uses this as well. \$\endgroup\$
    – pacmaninbw
    Jun 26, 2019 at 14:32
  • \$\begingroup\$ @pacmaninbw Sure, I can add that, but it's a single line. \$\endgroup\$
    – Zly Martin
    Jun 26, 2019 at 14:46
  • \$\begingroup\$ I guess I wasn't all that clear, how about the entire class. \$\endgroup\$
    – pacmaninbw
    Jun 26, 2019 at 14:48
  • \$\begingroup\$ @pacmaninbw No I cannot do that. I also do not see how that would help, as I'm asking for this method in particular. The surrounding class just reads/writes data to a file. This method is called twice, once to encrypt the password and once to decrypt it. \$\endgroup\$
    – Zly Martin
    Jun 26, 2019 at 14:58
  • \$\begingroup\$ Basically, we only have to guess a small number (the sum of letters) in order to break this key. Assuming that the password contains a–zA–Z0–9 characters and has N size, we can break the key in 62*N attempts at max, while it would take ut to 62^N attempts to guess the password!! So this is a really weak KDF and it significantly reduces the strength of your password. The simplest (but least preferable) KDF is PBKDF1, which just uses a hash in a loop. It is very easy to implement but it requires a hash function. \$\endgroup\$
    – t.m.adam
    Jun 26, 2019 at 19:08

2 Answers 2

3
\$\begingroup\$

I'd suggest using built-in libraries, especially if you're looking to solve something for production/industry. Quick search shows that there are two to consider that should be available within JDK, the JCA (Java Cryptography Architecture) for local storage of secrets, and JSSE (Java Secure Socket Extension) for comunicating with the remote service.

The JCA is a major piece of the platform, and contains a "provider" architecture and a set of APIs for digital signatures, message digests (hashes), certificates and certificate validation, encryption (symmetric/asymmetric block/stream ciphers), key generation and management, and secure random number generation, to name a few...

The Java Secure Socket Extension (JSSE) enables secure Internet communications. It provides a framework and an implementation for a Java version of the SSL, TLS, and DTLS protocols and includes functionality for data encryption, server authentication, message integrity, and optional client authentication...

Utilizing the documentation for local encryption, and examples scattered about, may cause one to write code similar to...


//  Helpful resources
// https://docs.oracle.com/javase/8/docs%2Ftechnotes%2Fguides%2Fsecurity%2Fcrypto%2FCryptoSpec.html#PBEEx
// https://stackoverflow.com/questions/4644415/java-how-to-get-input-from-system-console
// https://www.tutorialspoint.com/check-if-a-string-is-empty-or-null-in-java
// https://stackoverflow.com/questions/18142745/how-do-i-generate-a-salt-in-java-for-salted-hash


import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.SecretKeyFactory;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;

import java.security.SecureRandom;

import java.io.Console;


class pass_enc {

  public static void main(String[] args) throws Exception {
    Console console = System.console();
    if (console == null) {
      System.out.println("Unable to fetch console!");
      return;
    }

    PBEKeySpec pbeKeySpec;
    PBEParameterSpec pbeParamSpec;
    SecretKeyFactory keyFac;

    // Salt
    byte[] salt = new SecureRandom().nextBytes(salt);

    // Iteration count
    int count = 1000;

    // Create PBE parameter set
    pbeParamSpec = new PBEParameterSpec(salt, count);

    // Prompt for encryption password
    // Use char array because String objects are immutable
    // Convert to SecretKey object using PBE key factory
    char[] password = console.readPassword("Enter a passphrase: ");
    // char[] password = System.console.readPassword("Enter a passphrase: ");
    pbeKeySpec = new PBEKeySpec(password);
    keyFac = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_256");
    SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);

    // Create PBE Cipher
    Cipher pbeCipher = Cipher.getInstance("PBEWithHmacSHA256AndAES_256");

    // Initalize PBE Cipher with key and parameters
    pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);

    // Cleartext to be encrypted
    byte[] cleartext = "This is an example".getBytes();

    // Encrypt cleartext
    byte[] ciphertext = pbeCipher.doFinal(cleartext);

  }

}

... though the compiler will complain about salt being void; Java isn't a language that I write a whole lot in but perhaps the above code will get ya a little closer to something that preforms the necessary steps more securely. Edits and comments are certainly welcomed if someone does figure out how to get Oracle's example code working as intended.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Sorry for the (much) belated comment on this. This had definitely moved me much further in the right direction, showcasing packages and some use-examples of them that are native-available in Java. However, I cannot accept this as the answer as it showcases how to encrypt text using a (salted) password, rather than encrypting a password, though I could extend this for a local password store with a master pass. Hmm.... \$\endgroup\$
    – Zly Martin
    Jan 16, 2020 at 17:16
  • \$\begingroup\$ Thanks for the feedback, and please don't stress about timing. The "This is an example" string will need substituted for the data-base passphrase to be encrypted and stored. For brevity and not being certain on how cipher-text will be stored, I didn't include passing the example string as an parameter, nor writing to storage. Functions for reading stored data-base passphrase and decryption are likely needed to make this answer complete. But you're now on a more secure path, so feel free to ping (@) me if ya get stuck. \$\endgroup\$
    – S0AndS0
    Jan 16, 2020 at 20:01
1
\$\begingroup\$

As you stated, you probably shouldn't roll your own. Have you considered using a key vault?

https://www.vaultproject.io/

\$\endgroup\$
4
  • \$\begingroup\$ The linked "vaultproject" seems like a centralized secret manager? That goes way and beyond the needs here. Seems akin to replacing batteries with a connection directly to mains, if you don't mind the electrician analogy. This program just needs to somewhat secure a user's password for them on their local machine as the program is also running locally. It just makes a couple DB calls when it needs to, on the one connection made using the password. An enterprise-level key vault seems overkill. \$\endgroup\$
    – Zly Martin
    Jun 26, 2019 at 15:28
  • 2
    \$\begingroup\$ Nah, using off-the-shelf software isn't overkill. It's completely secure and requires practically no maintenance. You can't say that same about stuff you roll for yourself! What are you trying to conserve by not using COTS? \$\endgroup\$
    – Slothario
    Jun 26, 2019 at 15:36
  • \$\begingroup\$ I don't need, or want, a central collection of this information. It should only be storing the local computer user's credentials locally. This is in a business environment as well, maybe I should add that to the question. But this is a locally-ran program connecting to a DB on the network. It just seems like, by using the "vaultproject" or similar vaults that, even though it is likely lightyears ahead of anything I could roll myself, it's way ridiculous for the small projects this would be a part of. 1/2 \$\endgroup\$
    – Zly Martin
    Jun 26, 2019 at 15:47
  • \$\begingroup\$ The only thing I'm trying to conserve here is scope. I'd need a server, licenses (maybe?), recode the function into making all these other types of calls, add many LOCs to this, but would also aggregate all the user/pass of the people that may use this program. 2/2 \$\endgroup\$
    – Zly Martin
    Jun 26, 2019 at 15:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.