2

I am creating an algorithm where it has to encrypt and decrypt passwords by using JCE and AES. I will keep the encrypted text into a database, however I'm facing two problems. The first problem being that the encrypted text is returning weird symbols and not letters, like "c~ù▼=¡£¡?Å♠?¡Ç²?", but it returns the correct normal text. The second problem is that the algorithm would return a different encrypted text every time and if I were to save the encrypted text into a database and decrypt it later, would it still work? Or would I have to store the value as byte[] or String.

public static void main(String[] args) throws Exception {

    byte[] text1 = "welcome back".getBytes();
    byte[] text2 = "hello guys".getBytes();

    KeyGenerator gen = KeyGenerator.getInstance("AES");
    SecretKey key = gen.generateKey();

    Cipher x = Cipher.getInstance("AES");
    x.init(Cipher.ENCRYPT_MODE, key);
    byte[] encryptedText1 = x.doFinal(text1);
    byte[] encryptedText2 = x.doFinal(text2);

    x.init(Cipher.DECRYPT_MODE,key);
    byte[] decryptedText2 = x.doFinal(encryptedText2);
    byte[] decryptedText1 = x.doFinal(encryptedText1);
    System.out.println(new String(encryptedText1));
    System.out.println(new String(encryptedText2));
    System.out.println(new String(decryptedText1));
    System.out.println(new String(decryptedText2));
}

The results are something like this:

c~ù▼=¡£¡?Å♠?¡Ç²?
àÉ?<-??Yò*?b?]?
welcome back
hello guys

Is there anything I can do to fix these problems? Thank you very much.

Slaw
  • 37,820
  • 8
  • 53
  • 80
bem
  • 21
  • 4
  • 1
    Those "weird symbols" are not problematic. That's the sort of thing you can _expect_ to see when a random sequence of bytes is decoded into characters, no matter which encoding you use. – Slaw Jul 08 '22 at 06:32
  • 1
    In addition to the problems addressed in the accepted answer, you should also specify mode and padding when instantiating the `Cipher` instance, otherwise provider-dependent default values will be used. This can lead to incompatibilities between encryption and decryption (if different defaults are applied) or even cause vulnerabilities. In your environment, for instance, ECB is used as mode. This is an insecure mode that should generally not be applied. Also note that passwords are generally not encrypted, but hashed. – Topaco Jul 08 '22 at 07:48

3 Answers3

2

Second Problem

The second problem is that the algorithm would return a different encrypted text every time and if I were to save the encrypted text into a database and decrypt it later, would it still work?

Not completely sure I understand what the problem is. At least for the code in your question, for the same key and same input bytes, you'll get the same encrypted bytes out. But you must use the same key, both when encrypting and decrypting. And this means you need to (securely!) store the key somewhere.

If you're worried about seeing different output each time you run your example code, then that's because you generate a new key each time.

This can get complicated though, and I'd like to state I'm not a cryptography expert. I believe to use symmetric encryption properly, you want to avoid the same plain text resulting in the same cipher text. There are also different modes of AES, requiring different approaches. My point is that you should either do a lot of research, or hire an expert (if not both).


First Problem

The first problem being that the encrypted text is returning weird symbols and not letters, like "c~ù▼=¡£¡?Å♠?¡Ç²?", but it returns the correct normal text

Those "weird symbols" are not indicative of a problem. This is the sort of thing you should expect to see when you attempt to decode a random sequence of bytes into characters.

Your code first encodes a sequence of characters (the String) into a sequence of bytes (a byte[]). You then encrypt this sequence of bytes and receive a new essentially random sequence of bytes back. The encrypted bytes do not represent a sequence of characters anymore. Attempting to decode these encrypted bytes into a sequence of characters can yield anything from the charset used, including the "fallback/error" character. This is okay though because, again, the encrypted bytes do not represent a sequence of characters.

When you decrypt the encrypted bytes, you get the original sequence of bytes back. The decrypted bytes do, of course, represent a sequence of characters, and thus can be decoded into those characters. Note you must use the same charset when encoding and decoding the plain text in order to guarantee correctness. To ensure this, you should specify a charset. For example:

// encode
byte[] bytes = "Hello, World!".getBytes(StandardCharasets.UTF_8);
// decode
String string = new String(bytes, StandardCharsets.UTF_8);

Note this does not attempt to decode a sequence of encrypted bytes. It decodes the original bytes, the same bytes you'd get back after decrypting the encrypted bytes.

If you want to view the encrypted bytes as a String, then encoding those bytes into Base64 is a common solution. Base64 can also be used for other non-character bytes, such as image files, or even bytes representing characters.

byte[] bytes = "Hello, World!".getBytes(StandardCharasets.UTF_8);
byte[] encryptedBytes = encrypt(bytes);
String base64Text = Base64.getEncoder().encodeToString(encryptedBytes);

Note if you store the base64Text, then when you go to decrypt, you'll first need to decode the Base64 bytes into the original encrypted bytes.

Slaw
  • 37,820
  • 8
  • 53
  • 80
  • @bem I modified the first section of my answer a little. Your current code will give the same cipher text given the same plain text and same key, and you still need to securely store the key somewhere, but cryptography is complicated and there are a lot of pitfalls you can run into. To correctly use cryptography requires a lot of research. And to really make sure an implementation is correct typically requires reviews from preferably a lot of subject-matter experts. I am _not_ a cryptography expert. – Slaw Jul 08 '22 at 07:40
1

Most if not all enryptions algorithm and (secure) hash functions just produce bytes, without any encoding as what they are producing is not text but literally just a byte[].

Most of the time these bytes are showns as base64 encoded in a tutorial or when saved in a textual representation - see here on how to do this with Java 8+: https://stackoverflow.com/a/26897706/4934324

roookeee
  • 1,710
  • 13
  • 24
0

Because your algorithm have not any information about your plaintext's Charset. You have to set it when you're getting bytes.

byte[] text1 = "welcome back".getBytes(StandardCharsets.UTF_8);
byte[] text2 = "hello guys".getBytes(StandardCharsets.UTF_8); 

And for printing:

System.out.println(new String(encryptedText1, StandardCharsets.UTF_8));
System.out.println(new String(encryptedText2, StandardCharsets.UTF_8));

This approach should work.

But if do you want to see something you can read use Base64 encoder.

System.out.println(Base64.getEncoder().encodeToString(encryptedText1));
System.out.println(Base64.getEncoder().encodeToString(encryptedText2));
Halil Sahin
  • 568
  • 1
  • 6
  • 25
  • I just tried the code again but somehow it still returns back some weird symbols for the encrypted text. – bem Jul 08 '22 at 06:25
  • In this case, the encoding shouldn't be the problem, because both `#getBytes()` and `#String(byte[])` use the default charset (i.e., they use the same encoding). – Slaw Jul 08 '22 at 06:28
  • I believe (at least part of) the real problem is the mistaken belief that the "strange symbols" indicate a problem. – Slaw Jul 08 '22 at 06:30
  • Oh, so it's normal for the encrypted text to be those types of symbols right? – bem Jul 08 '22 at 06:32
  • Ohhh, okay thank you very much. Is there a way that you can specify a charset though? – bem Jul 08 '22 at 06:43
  • Ohh okay thank you guys so much. I have one more question though, how does charset relate to Base64? – bem Jul 08 '22 at 07:09
  • A charset is a definition of how each character of a String is represented / saved in bytes. Base64 is a way to encode bytes (doesn't matter if its bytes of text or anything else) into a String that encodes bytes into one of 64 different characters until all bytes are encoded - see wikipedia for more https://en.wikipedia.org/wiki/Base64#:~:text=Base64%20is%20also%20widely%20used,by%20the%20inserted%20line%20breaks). – roookeee Jul 08 '22 at 07:15
  • `new String(encryptedText1, StandardCharsets.UTF_8)` makes the incorrect assumption that the random bytes resulting from encryption can be turned into a valid string. It will be turned into a string, but one that has essentially been corrupted. – President James K. Polk Jul 08 '22 at 13:04