Encrypt/Decrypt a string


#1

Hey

Please can someone assist me? What I’m trying to do is find a way to encrypt a string retrieved from a text editor. Basically a user sets a password in the program and when they click the appropriate button, the program encrypts the string they entered. I’ll then save it to a text file of sorts. However the program needs to be able to retrieve the encrypted string, decrypt it and then make use of it. I was looking at a blowfish algorithm, but don’t know how I’d use it with a String. Any assistance would be greatly appreciated!


BlowFish String Encryption
#2

Do you need to be able to retreive the password in clear text? Otherwise a hash might be a better option. I’m not sure, but I think there are hash function right in the string class.


#3

Well, in a settings component, there is a text editor which reads a text file and populates the text editor (although *'s are shown). If no file is present, it’s left blank. When the user types something in there and presses the “OK” button, the string is taken from the password text editor, encrypted and then saved to the text file. This is so that when the program is opened and closed, the user doesn’t have to retype in all the settings, as well as making sure that if someone opens the text file the password isn’t instantly visible.


#4

depending on how secure this needs to be, as rock says, a hash will do fine.

Store the md5 hash or similar in your settings file, and when the user enters their password, encrypt it in the same manner, and compare the hash with your stored hash.


#5

The password is to a database. If I use a hash as you said, the password would not be able to change if the database one does unless I recompile the program. I’ll use the password to login to the database, and if it doesn’t work then an error will be thrown.

Oh, and I don’t need something uncrackable… just something to obscure the text in the settings file so if someone opens it they won’t be able to read the password.


#6

The BlowFish class should do the trick for you.


#7

But from what I can see, the blowfish class of yours only takes arguments of uint32 type, not String. :?

Edit:
I’ve just seen the String getIntValue() function. This would change the string into an int for use with the blowfish class, but how would I change the int back into a String?


#8

no, basically getIntValue() try to convert the string to a int if it really represent a integer in characters… so String(“5”). getIntValue() basically returns 5… just split your password letters from “tchar” to “uint32” and encrypt em…


#9

Or turn your string into a MemoryBlock and encrypt that.


#10

How would you go about doing that?

Interesting. Why would you do that? How would you encrypt a MemoryBlock?


#11

What would be fantastic is if someone could give me an example of how I’d use the Blowfish class and feed it a String. Would be very much appreciated!


#12

I needed this function and couldn’t find it, so here’s my solution. First the class declaration:

class Crypto { public: static String encrypt(String plaintext); static String decrypt(String ciphertext); };

Next, is the key which was generated by a real h/w random number generator:

#define KeySize 72 static uint8 cryptokey[KeySize] = { 117,129,49,21,6, 108,245,113,110,240, 173,174,17,158,14, 116,39,120,34,242, 74,43,101,9,179, 212,218,203,145,1, 63,24,56,68,67, 253,114,195,178,213, 238,67,20,144,11, 208,115,49,234,231, 186,228,44,246,118, 213,213,75,112,126, 216,178,216,138,139, 85,200,251,16,30, 175,170 };

Finally, here’s the implementation of the encrypt and decrypt methods:

[code]
/*

  • encrypts a string using the BlowFish algorithm
  • If the string has an even number of characters,
  • then the letters ‘ev’ are appended before encrypting.
  • If the string has an odd number of characters,
  • then the letter ‘o’ is appended before encrypting.
  • This allows decrypt to determine if the original
  • string had an odd number of characters or not.
  • This is necessary because BlowFish encrypts pairs of ints

*/
String Crypto::encrypt(String plaintext)
{
BlowFish bf(cryptokey, KeySize);
int len = plaintext.length();

// make buffers an even length so we always have left and right ints
if(len % 2 == 0)
{
	plaintext += "ev";
	len += 2;
}
else
{
	plaintext += "o";
	len += 1;
}
String cyphertext = String::empty;


for(int i = 0; i < len; i += 2)
{
	uint32 l = (uint32)plaintext[i];
	uint32 r = (uint32)plaintext[i+1];
	bf.encrypt(l, r);
	cyphertext += String::formatted(T("%08x"), l) + String::formatted(T("%08x"), r);
}

return cyphertext;

}[/code]

[code]/*

  • decrypts a string using the BlowFish algorithm
  • If the original plaintext string hasd an even number of characters,
  • then the letters ‘ev’ were appended before encrypting.
  • If the string had an odd number of characters,
  • then the letter ‘o’ was appended before encrypting.
  • This allows decrypt to determine if the original
  • string had an odd number of characters or not.
  • This is necessary because BlowFish encrypts pairs of ints

*/
String Crypto::decrypt(String ciphertext)
{
BlowFish bf(cryptokey, KeySize);
int len = ciphertext.length();
String plaintext = String::empty;
for(int i = 0; i < len; i += 16)
{
String hex = ciphertext.substring(i, i+8);
uint32 l = hex.getHexValue32();
hex = ciphertext.substring(i+8, i+16);
uint32 r = hex.getHexValue32();
bf.decrypt(l, r);
//concatenate, but ignore 0s (which may arise because we padded everything to 8 bytes
if(l != 0)
plaintext += String::createStringFromData(&l, 4);
if(r != 0)
plaintext += String::createStringFromData(&r, 4);
}
// adjust plain text by removing appended ‘ev’ or 'o’
len = plaintext.length();
if(plaintext[len-1] == ‘o’)
plaintext = plaintext.substring(0, len-1);
else
plaintext = plaintext.substring(0, len-2);
return plaintext;
}[/code]


#13

Here’s my go at it. Similar to yours, except it does padding with zeroes at the end - seems to do the job well for arbitrary string legths.

StringEncryptor.h:


#ifndef __STRINGENCRYPTOR_H__
#define __STRINGENCRYPTOR_H__

#include "juce.h"

//==============================================================================

/**
  A simple class for encrypting and decrypting string data types 
  
  Example: 
  @code
  StringEncryptor se ("This is my key");
  String enc = se.encryptString (String ("Password"));
  AlertWindow::showMessageBox (AlertWindow::InfoIcon, "Blowfish encrypt", enc);
  String temp = String (T("[")) + se.decryptString (enc) + String T("]") ;
  AlertWindow::showMessageBox (AlertWindow::InfoIcon, "Blowfish decrypt", temp);
  @endcode
*/
class StringEncryptor: public BlowFish 
{
public:
	/**
		Initialise the encryptor with a key string, which will be used for 
		encryption and decryption as well. 

		@param	strKey	Encryption key
	*/
	StringEncryptor(const String& strKey);

	/**
		Default destructor. 
	*/
	~StringEncryptor() {};

	/**
		Encrypt any non-empty string. The resulting string will be padded to the
		nearest 16-character length.

		@param	str	The string to encrypt
	*/
	const String encryptString (const String& str);

	/**
		Decrypt a previously encrypted string. Returns an empty string is the parameter 
		doesn't look like an encrypted string.

		@param	str	The encrypted string we want to convert back
	*/
	const String decryptString (const String& str);
};

#endif   // __STRINGENCRYPTOR_H__

StringEncryptor.cpp:


#include "StringEncryptor.h"

StringEncryptor::StringEncryptor(const String& strKey)
: BlowFish ((uint8 *)((const char *)strKey), strKey.length()) 
{
}

const String StringEncryptor::encryptString (const String& str)
{
	if (!str.length()) 
		return String::empty;

	int iBlocksize = ((str.length()+7) / 8) * 8;
	MemoryBlock mb (iBlocksize, true);
	mb.copyFrom ((const char *)str, 0, str.length());
	for (int n=0; n<iBlocksize/8; n++) {
		encrypt ((uint32&)mb[n*8], (uint32&)mb[n*8+4]);
	}
	String strEncrypted = String::empty;
	for (int i = 0; i<iBlocksize; i++) {
		strEncrypted << String::formatted (T("%02x"), (unsigned char)mb[i]);
	}
	return strEncrypted;
}

const String StringEncryptor::decryptString (const String& str)
{
	// If encrypted string is empty or it the length is not 
	// multiples of 16, something is wrong
	if (!str.length() || (str.length() % 16))
		return String::empty;

	int iBlocksize = str.length() / 2;

	// Make sure there's a \0 at the end of the string converted from mb
	MemoryBlock mb (iBlocksize+1, true);
	
	for (int i = 0; i<str.length()/2; i++) {
		mb[i] = (char)(str.substring (i*2, i*2+2).getHexValue32());
	}
	for (int n=0; n<iBlocksize/8; n++) {
		decrypt ((uint32&)mb[n*8], (uint32&)mb[n*8+4]);
	}

	return String::createStringFromData (mb.getData(), iBlocksize+1);
}

MemoryBlock::operator[] Y U NO check offset < size?
#14

thanks for the code snippets.

Is it normal that the String length would increase?
My original String length is 98778.

The StringEncryptor result length is 197568. (twice the size)
mrblasto’s result length is 790240. (eight times the size)

Both using the same simple key “x9e”.

Edit: encryption of that String takes about 5 seconds, decryption about 30 - is that normal?


#15

The example in CryptographyDemo doesn't work with arbitrary strings (e.g. no white spaces).

Here is my take on it.

Encode:

String encodeString (const String& plain)
{
    RSAKey publicKey {"11,c550a446aa8f8296ec7b94f5176bae80ac3869caebe8d79ae7c2fd6aad44c8cb20eea0ad3b4c648a9201798b6bd20f63e34cfd0afa21d1d93a5b7ad96a5fab79"};
    
    CharPointer_UTF8 utf8 = plain.toUTF8();
    CharPointer_UTF8::CharType* utf8Address = utf8.getAddress();
    MemoryBlock plainMemoryBlock (utf8Address, utf8.sizeInBytes());
    
    BigInteger sourceInteger;
    sourceInteger.loadFromMemoryBlock (plainMemoryBlock);
    
    if (!sourceInteger.isZero())
    {
        // Encode
        BigInteger encodedInteger (sourceInteger);
        publicKey.applyToValue (encodedInteger);
        
        MemoryBlock encodedMemoryBlock = encodedInteger.toMemoryBlock();
        
        return encodedMemoryBlock.toBase64Encoding();
    }
    else
    {
        return String::empty;
    }
}

Decode:

String decodeString (const String& encoded)
{
    RSAKey privateKey {"a27ea5675f490221efed4d7e8bc2173cca10571f951a1af80a281bfd7fa20ec3f3fcde32d23827e544b36cd6ce6fb6c0d61e85e8e52ea509a5dec80d751b8951,c550a446aa8f8296ec7b94f5176bae80ac3869caebe8d79ae7c2fd6aad44c8cb20eea0ad3b4c648a9201798b6bd20f63e34cfd0afa21d1d93a5b7ad96a5fab79"};
    
    MemoryBlock encodedMemoryBlock;
    encodedMemoryBlock.fromBase64Encoding (encoded);
    
    BigInteger encodedInteger;
    encodedInteger.loadFromMemoryBlock (encodedMemoryBlock);
    
    if (!encodedInteger.isZero())
    {
        // Decode
        BigInteger decodedInteger (encodedInteger);
        privateKey.applyToValue (decodedInteger);
        
        MemoryBlock decodedMemoryBlock = decodedInteger.toMemoryBlock();
        
        return decodedMemoryBlock.toString();
    }
    else
    {
        return String::empty;
    }
}

#16