What I need is to encrypt string which will show up in 2D barcode(PDF-417) so when someone get an idea to scan it will get nothing readable.
Other requirements:
- should not be complicated
- it should not consist of RSA, PKI infrastructure, key pairs, etc.
It must be simple enough to get rid of the people snooping around, and easy to decrypt for other companies interested in getting that data. They call us, we tell them the standard or give them some simple key which can then be used for decryption.
Probably those companies could use different technologies so it would be good to stick to some standard which is not tied to some special platform or technology.
What do you suggest? Is there some Java class doing encrypt() decrypt() without much complication in achieving high security standards?
You might want to consider some automated tool to do the encryption / decryption code generation eg. https://www.stringencrypt.com/java-encryption/
It can generate different encryption and decryption code each time for the string or file encryption.
It's pretty handy when it comes to fast string encryption without using RSA, AES etc.
Sample results:
We use it all the time in our company.
Like many of the guys have already told, you should use a standard cypher that is overly used like DES or AES.
A simple example of how you can encrypt and decrypt a string in java using AES.
I'd recommend to use some standard symmetric cypher that is widely available like DES, 3DES or AES. While that is not the most secure algorithm, there are loads of implementations and you'd just need to give the key to anyone that is supposed to decrypt the information in the barcode. javax.crypto.Cipher is what you want to work with here.
Let's assume the bytes to encrypt are in
Next, you'll need the key and initialization vector bytes
Now you can initialize the Cipher for the algorithm that you select:
Encryption would go like this:
And decryption like this:
This is the encryption & decryption code I just wrote in Java 8 considering the following points. Hope someone would find this useful:
Encryption Algorithm: Block cipher AES with 256 bits key is considered secure enough. To encrypt a complete message, a mode needs to be selected. Authenticated encryption (which provides both confidentiality and integrity) is recommended. GCM, CCM and EAX are most commonly used authenticated encryption modes. GCM is usually preferred and it performs well in Intel architectures which provide dedicated instructions for GCM. All these three modes are CTR-based (counter-based) modes and therefore they do not need padding. As a result they are not vulnerable to padding related attacks
An initialization Vector (IV) is required for GCM. The IV is not a secret. The only requirement being it has to be random or unpredictable. In Java, the
SecuredRandom
class is meant to produce cryptographically strong pseudo random numbers. The pseudo-random number generation algorithm can be specified in thegetInstance()
method. However, since Java 8, the recommended way is to usegetInstanceStrong()
method which will use the strongest algorithm configured and provided by theProvider
NIST recommends 96 bit IV for GCM to promote interoperability, efficiency, and simplicity of design
To ensure additional security, in the following implementation
SecureRandom
is re-seeded after producing every 2^16 bytes of pseudo random byte generationThe recipient needs to know the IV to be able to decrypt the cipher text. Therefore the IV needs to be transferred along with the cipher text. Some implementations send the IV as AD (Associated Data) which means that the authentication tag will be calculated on both the cipher text and the IV. However, that is not required. The IV can be simply pre-pended with the cipher text because if the IV is changed during transmission due to a deliberate attack or network/file system error, the authentication tag validation will fail anyway
Strings should not be used to hold the clear text message or the key as Strings are immutable and thus we cannot clear them after use. These uncleared Strings then linger in the memory and may show up in a heap dump. For the same reason, the client calling these encryption or decryption methods should clear all the variables or arrays holding the message or the key after they are no longer needed.
No provider is hard coded in the code following the general recommendations
Finally for transmission over network or storage, the key or the cipher text should be encoded using Base64 encoding. The details of Base64 can be found here. The Java 8 approach should be followed
Byte arrays can be cleared using:
However, as of Java 8, there is no easy way to clear
SecretKeyspec
andSecretKey
as the implementations of these two interfaces do not seem to have implemented the methoddestroy()
of the interfaceDestroyable
. In the following code, a separate method is written to clear theSecretKeySpec
andSecretKey
using reflection.Key should be generated using one of the two approaches mentioned below.
Note that keys are secrets like passwords, but unlike passwords which are meant for human use, keys are meant to be used by cryptographic algorithms and hence should be generated using the above way only.
The encryption key can be generated primarily in two ways:
Without any password
With password
Update Based on Comments
As pointed out by @MaartenBodewes, my answer did not handle any
String
as is required by the question. Therefore, I'll make an attempt to fill that gap just in case someone stumbles upon this answer and leaves wondering about handlingString
.As indicated earlier in the answer, handling sensitive information in a
String
is, in general, not a good idea becauseString
is immutable and thus we cannot clear it off after use. And as we know, even when aString
doesn't have a strong reference, the garbage collector does not immediately rush to remove it off heap. Thus, theString
continues to be around in the memory for an unknown window of time even though it is not accessible to the program. The issue with that is, a heap dump during that time frame would reveal the sensitive information. Therefore, it is always better to handle all sensitive information in a byte array or char array and then fill the array with 0s once their purpose is served.However, with all that knowledge, if we still end up in a situation where the sensitive information to be encrypted is in a
String
, we first need to convert it into a byte array and invoke theencrypt
anddecrypt
functions introduced above. (The other input key can be generated using the code snippet provided above).A
String
can be converted into bytes in the following way:As of Java 8,
String
is internally stored in heap withUTF-16
encoding. However, we have usedUTF-8
here as it usually takes less space thanUTF-16
, especially for ASCII characters.Likewise, the encrypted byte array can also be converted into a String as below:
Here a simple solution with only
java.*
andjavax.crypto.*
dependencies for encryption of bytes providing confidentiality and integrity. It shall be indistinguishable under a choosen plaintext attack for short messages in the order of kilobytes.It uses
AES
in theGCM
mode with no padding, a 128bit key is derived byPBKDF2
with lots of iterations and a static salt from the provided password. This makes sure brute forcing passwords is hard and distributes the entropy over the entire key.A random initialisation vector (IV) is generated and will be prepended to the ciphertext. Furthermore, the static byte
0x01
is prepended as the first byte as a 'version'.The entire message goes into the message authentication code (MAC) generated by
AES/GCM
.Here it goes, zero external dependencies encryption class providing confidentiality and integrity:
Here the entire project with a nice CLI: https://github.com/trichner/tcrypt
Edit: now with appropriate
encryptString
anddecryptString