BlowFish String Encryption

Your encrypted memory block contains “random” bytes, but the toString method tries to decode it like an utf-8 string and this fails. You could use toBase64Encoding() to convert the raw bytes to a (odly) base64 encoded string.

String is simply not the right type to hold binary data. A std::string, initialized with begin and end of the memory block would work though. It depends on what you’re planning to do with the encrypted data.

Edit: https://juce.com/doc/classMemoryBlock#a77c0a3fcaf358311744d0998ab0e7b1b

Here is an implementation of BlowFish encryption, for Juce::Strings

// ==============================================================================
// 01-BLOWFISH Encryption method.
// ==============================================================================
/**
The key values for the BlowFish encryption method
*/
static const int BlowFishKeySize = 10;
static const uint8 BlowFishCryptoKey[BlowFishKeySize] =
{
15, 205, 127, 6, 92,
109, 16, 57, 137, 241
};

// ===================================================================================
// 03-ENCRYPT/DECRYPT UTILITIES
// ------------------------------
// Encrypt and decrypt the contents of registration-file, using BlowFish method
// ===================================================================================
struct EncryptUtils
{
/** -----------------------------------------------------------------------------
Encrypt the contents of registration/activation info
-------------------------------------------------------------------------------*/
static const String encryptKeys(const String JSON_Keys)
{
if (! JSON_Keys.length())
return String::empty;

  BlowFish Bf(BlowFishCryptoKey, BlowFishKeySize);
  String Keys_To_Encrypt = JSON_Keys;

  // make buffer to have an even length, so we always have left and right ints
  if (Keys_To_Encrypt.length() % 2 == 0)
  	Keys_To_Encrypt = Keys_To_Encrypt + "  ";
  else
  	Keys_To_Encrypt += " ";

  // Encrypt Keys-string
  String Encrypted_Keys = EncryptStringWithBlowFish(Keys_To_Encrypt);
  
  return Encrypted_Keys;

}

/** -----------------------------------------------------------------------------
Encrypt a Key-String and returns an Encrypted Key-String with BlowFish method
Note for “%08x” specifier :
8 : show every single char in a manner of 8 bits
0 : prefix each char with 0’s instead of just blank spaces
x : print each char in a hexadecimal code using lower-case letters.
-----------------------------------------------------------------------------*/
static String EncryptStringWithBlowFish(String Keys_To_Encrypt)
{
BlowFish Bf(BlowFishCryptoKey, BlowFishKeySize);
String Encrypted_Keys = String::empty;
int Len = Keys_To_Encrypt.length();

  for (int i = 0; i < Len; i += 2)
  {
  	uint32 left = (uint32)Keys_To_Encrypt[i];
  	uint32 right = (uint32)Keys_To_Encrypt[i + 1];
  	Bf.encrypt(left, right);
  	Encrypted_Keys += String::formatted(("%08x"), left) + String::formatted(("%08x"), right);
  }

  return Encrypted_Keys;

}

/** -----------------------------------------------------------------------------
Decrypt the keys-String
-------------------------------------------------------------------------------*/
static String decryptKeys(String& JSON_Keys)
{
String Decrypted_Keys = String::empty;

  // Decrypt keys-string
  Decrypted_Keys = decryptStringFromBlowFish(JSON_Keys);

  // adjust plain text by removing appended ' ' or ' '
  int Len = Decrypted_Keys.length();
  if (Decrypted_Keys[Len - 1] == ' ')
  	Decrypted_Keys = Decrypted_Keys.substring(0, Len - 1);
  else
  	Decrypted_Keys = Decrypted_Keys.substring(0, Len - 2);

  return Decrypted_Keys;

}

/**-----------------------------------------------------------------------------
Decrypt the contents of registrtion-file which preveously encrypted
by the BlowFish method
-----------------------------------------------------------------------------*/
static String decryptStringFromBlowFish(String Keys_To_Decrypt)
{
BlowFish Bf(BlowFishCryptoKey, BlowFishKeySize);
String Decrypted_Keys = String::empty;
int Len = Keys_To_Decrypt.length();

  for (int i = 0; i < Len; i += 16)
  {
  	String hex = Keys_To_Decrypt.substring(i, i + 8);
  	uint32 left = hex.getHexValue32();
  	hex = Keys_To_Decrypt.substring(i + 8, i + 16);
  	uint32 right = hex.getHexValue32();
  	Bf.decrypt(left, right);

  	//concatenate, but ignore 0s (which may arise because we padded everything to 8 bytes
  	if (left != 0)
  		Decrypted_Keys += String::createStringFromData(&left, 4);
  	if (right != 0)
  		Decrypted_Keys += String::createStringFromData(&right, 4);
  }

  return  Decrypted_Keys;

}
}; // end of struct EncryptUtils

I hope this help you to understand how the BlowFish works. . .

1 Like

I’m doing nothing special with the data. I want to be able to grab a string from a juce::TextEditor and convert it into gibberish, then back again. Nothing big deal.

I honestly thought it would be easier to do something like this in JUCE. I figured that many people would be in need of the ability to encrypt a String, but I’m most likely way off in my assumption, otherwise it would probably be implemented by now.

I know it would never happen, but it would be awesome to have a class method in BlowFish that you just pass in a String of Text, a String for the Key, and it returns an encrypted string.

Thank you very much for this. I will pull this into my editor and study it. Thank you.

Please read my comment above. You probably just want to use toBase64Encoding() instead of toString().

1 Like

Yes, I switched it toString() to toBase64Encoding() and there are no more errors being produced, however, I’m having trouble making the algorithm symmetric. BlowFish is symmetric, so if I can get my algorithm correct, it should be able to convert a non-encrypted String to an encrypted string and back, correct?

String encryptStringWithBlowFish (const String &key, const String &textToEncrypt)
{
    MemoryBlock memoryBlock;
    memoryBlock.loadFromHexString (String::toHexString (textToEncrypt.getHexValue64()));
    
    BlowFish blowFish (key.toUTF8(), (int) key.toUTF8().sizeInBytes());
    blowFish.encrypt (memoryBlock);
    
    return memoryBlock.toBase64Encoding();
}

Once again, my apologies for lacking the knowledge of BlowFish and MemoryBlock.

I think your problem may lie here. getHexValue64 returns a single int64, not a hex string of the data.
I would have thought the easiest thing to do is simply create a MemoryOutputStream to the MemoryBlock and write the textToEncrypt to it that way?

1 Like

I am a little bit confused, I figured that using:

String::toHexString (textToEncrypt.getHexValue64());

would return a hex string. I realize that getHexValue64() returns an int64, but I thought that once that it is passed into the static method String::toHexString(), then a hex string would return. Though I probably misunderstood what the IntegerType Number parameter of the toHexString() method meant.

Also, thanks for the tip, I’ll look into the MemoryOutputStream class.

Yeah, my general issue with JUCE is that, most of the time, I don’t know that certain items even exist within the library, so I can’t use them because I don’t even know of them (because it is so massive). Or, if I know what I need to do, finding the correct class might be a bit of a challenge at times if the categories and labels in my head aren’t the same as the wording of the actual class. These are not issues with JUCE, but mostly my lack of familiarity with it.

1 Like

Here’s a quick example, not sure exactly if this is your use case but works:

    const String encryptionKey ("acbdefg123");
    const String textToEncrypt ("Hello World!");

    MemoryBlock mb;
    MemoryOutputStream os (mb, false);
    os << textToEncrypt;

    DBG("BEFORE ENCRYPTION:\n\t" + mb.toString());

    BlowFish bf (encryptionKey.toUTF8(), (int) encryptionKey.getNumBytesAsUTF8());
    bf.encrypt (mb);

    DBG("ENCRYPTED_B64:\n\t" + mb.toBase64Encoding());

    bf.decrypt (mb);
    DBG("DECRYPTED:\n\t" + mb.toString());
2 Likes

Your algorithm seems to work on my end! I will try to break this into two separate functions for encrypting and decrypting and will report back. Thanks a ton.

Sorry to bother you again, but what would be the best way to load the encrypted string back into a memory block? I’ve broken your code up into encrypt and decrypt functions:

String encryptStringWithBlowFish (const String &key, const String &textToEncrypt)
{
    MemoryBlock mb;
    MemoryOutputStream os (mb, false);
    os << textToEncrypt;
    
    BlowFish blowFish (key.toUTF8(), (int) key.getNumBytesAsUTF8());
    blowFish.encrypt (mb);

    return mb.toBase64Encoding();
}

String decryptStringWithBlowFish (const String &key, String &textToDecrypt)
{
    MemoryBlock mb;
    MemoryOutputStream os (mb, false);
    os << textToDecrypt;
    
    BlowFish blowFish (key.toUTF8(), (int) key.getNumBytesAsUTF8());

    blowFish.decrypt (mb);

    return mb.toString();
}

This causes a problem since the original MemoryBlock that was encrypted is destroyed, so I have to reload it again with the encrypted text somehow, but I still get an error on the assertion when trying to decrypt it, so I feel as if I’m not loading the encrypted data correctly into the MemoryBlock.

This is the root cause of all your problems, if I understand you correctly:

  • You can easily represent a string by a memory block
  • You cannot represent a memory block as string,

because in a memory block special characters can occur, like

  • ASCII(0) → terminates your string, even though the memory goes on
  • ASCII(>127) → unicode byte, only allows specific characters following the one (google UTF-8 or multibyte character)

Hence you need an encoding (e.g. base64 that @dave suggested), to represent all numbers in the encoded memory, or you simply save it as binary data.

What is important, that you always keep encoding and decoding symmetrical. A common mistake is to accidentally run encode or decode twice, which leads to gibberish.

Also make sure to use the encoding and decoding from the same package, if possible. There was e.g. a base64 encoding in the codebase, that was slightly different (see this Base64 thread…)

Whenever you call MemoryBlock::toString() (also when using the operator<<() ), it will assert, if there are invalid characters, and the chances are quite high, as you painfully found out already…

Hope that made the assert a little more clear

I am familiar with ASCII characters (printables and non-printables) and null-terminated strings, but I just didn’t have any understanding of MemoryBlock and the conversions between String and MemoryBlock. I’ll see if I can figure it out based on the information you provided. Thanks.

I think you helped me find the correct solution:

String encryptStringWithBlowFish (const String &key, const String &textToEncrypt)
{
    MemoryBlock memoryBlock;
    memoryBlock.loadFromHexString (String::toHexString(textToEncrypt.toUTF8(), (int) textToEncrypt.getNumBytesAsUTF8()));
    
    BlowFish blowFish (key.toUTF8(), (int) key.getNumBytesAsUTF8());
    blowFish.encrypt (memoryBlock);
    
    return memoryBlock.toBase64Encoding();
}

String decryptStringWithBlowFish (const String &key, const String &textToDecrypt)
{
    MemoryBlock memoryBlock;
    memoryBlock.fromBase64Encoding (textToDecrypt);
    
    BlowFish blowFish (key.toUTF8(), (int) key.getNumBytesAsUTF8());
    blowFish.decrypt (memoryBlock);
    
    return memoryBlock.toString();
}

This produces zero errors and encrypts and decrypts correctly (from my limited testing).

Edit: Was able to completely remove using MemoryOutputStream

1 Like
memoryBlock.loadFromHexString (String::toHexString(textToEncrypt.toUTF8(), (int) textToEncrypt.getNumBytesAsUTF8()));

Converting a string to hex so that you can immediately convert it back from hex is pretty horrible. Dave’s suggestion above is a much better way of thinking about the task.

1 Like

Alright, I went back to using the MemoryOutputStream, as previously suggested.

Based on the results from the searching I did, It seems there were a handful of posts from others all wishing to have a simple and intuitive String encryption method, would this be something that would ever happen?

In my experience, encryption is never simple or intuitive!

The problem with a request like that is that it’s not the responsibility of the BlowFish class to know anything about strings or to make assumptions about the format of data you might choose to feed into it. And there are a heap of options for how you might choose to encode a string.

If it wasn’t trivial to write as a free function, then maybe it’d be justified, but come on, it’s not rocket science! Tthis thread seems to be making it all sound a lot more difficult than it really is! all you need is e.g.

static MemoryBlock encodeString (BlowFish& blowfish, StringRef text)
{
    MemoryBlock mb;

    {
        MemoryOutputStream out (mb, false);
        out << text;
    }

    blowfish.encrypt (mb);
    return mb;
}

static String decodeString (BlowFish& blowfish, MemoryBlock mb)
{
    blowfish.decrypt (mb);
    return mb.toString();
}
1 Like

Well, I never suggested it would be added to the BlowFish class, just that having a method somewhere might be useful, but I understand the reason for not including one at all - fair enough.

You are correct. This isn’t rocket science, but understanding what intermediate classes to use to get data from point A to point B isn’t always instantly easy for those of use who have never touched these classes before and are still unfamiliar with a vast majority of the API (for example I had no knowledge of MemoryOutputStream before the above recommendation, so I wouldn’t know to use it). 11 years since this post and beginners are still having difficulty putting these pieces together, as there are multiple attempts in this thread to do the same task. I suppose you could say we need study more before posting questions that might seem absolutely elementary to people of higher caliber and wasting the member’s time, but you have to remember that this is the welcome message of your forums:

Anyway, back on track: what is interesting is at one point, one of my first attempts at this did the same as yours and returned a MemoryBlock from encrypt and accepted a MemoryBlock in decode, as it made sense to maintain that specific block of memory and not destroy it.

Sorry, I wasn’t having a dig at you personally - just getting annoyed at this thread in general, as it’s become filled with so much faffing around that it makes a pretty straightforward task seem way harder than it actually is! Any beginner coming here to learn about blowfish encryption would come away much more confused than if they just figured it out themselves!

2 Likes

Sorry for the mediation.
I think the real problem is not the encryption method itself.
I think it is the manipulation of data after their encryption.
As we know, the BlowFish encryption method, produces illegal characters, so the real problem is to manipulate these characters after the encryption, in order to store somewhere the encrypted data.
Therefore, I think that any help should be given to this issue.

1 Like