I am trying to implement AES in Java (for academic purpose only).
I took default implementation to compare my result with original.
private byte[] crypt(int mode, byte[] key, byte[] initializationVector, byte[] data)
{
try
{
SecretKeySpec secretKeySpec = new SecretKeySpec(key, AES);
IvParameterSpec ivParameterSpec = new IvParameterSpec(initializationVector);
cipher.init(mode, secretKeySpec, ivParameterSpec);
return cipher.doFinal(data);
}
catch(BadPaddingException | InvalidKeyException | IllegalBlockSizeException | InvalidAlgorithmParameterException e)
{
throw new RuntimeException(e);
}
}
Where int mode is Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE.
Note, that key, iv and data are all byte[] (and not char[]).
Now, when I am trying to do my implementation I face problem with Sboxes.
I took array from wikipedia
https://en.wikipedia.org/wiki/Rijndael_S-box
In example, there is unsigned char (example is for C++, not for Java).
The problem is: byte in Java is signed (-128 to 127), so half of them is negative, what causes trouble, when I want to use it later as index of array.
If I will convert this sbox to byte[], then I have issue with array's indexes.
If I decide to do everything on char[], then I will double memory usage, plus it will be hard to compare my result with stock implementation (that uses byte[]).
I am sure, that guys who implemented stock encryption were much smarter than me, so I assume byte[] is correct an answer, but how do I handle negative array index in this case?
The only was I see is "sneaky" converting between byte and char when non-negative representation is required, with method like:
public class Sbox
{
// it's array from wiki, 256 elements
private final char substitute[] =
{
0x63, 0x7C, ... 0xBB, 0x16
};
public byte substitute(byte index)
{
return convert(substitute[convert(index)]);
}
private char convert(byte b)
{
return (char) (b < 0 ? b + 256 : b);
}
private byte convert(char c)
{
return (byte) (c < 256 ? c : 256 - c);
}
}
when needed index and back - this isn't most efficient idea, and looks like bug mine.
How it can be done better?
Should it be byte[] or char[] as default structure? (for key/iv/data)
And by the way, just to be certain, cipher.doFinal(data); (in stock implementation) is doing whole encryption, not just the final (non-standard) round?
Does stock implementation utilize AES-NI?
In case it matters: I am outside USA and I have unlocked unlimited crypto power version.
Related
The following is an extract from using AES encryption in Java:
encryptedData = encryptCipher.doFinal(strToEncrypt.getBytes());
The following is an extract in c#
DecryptStringFromBytes_Aes(encrypted, myAes.Key, myAes.IV);
Both use a byte array one to encrypt the other to decrypt, the encryption in Java encrypts producing some negative values stored in a byte array.
C# uses a byte array to decrypt but a byte in C# is defined as only containing the numbers from 0..255 - Java defines its Byte type as -128 to 127.
Therefore, I cannot send encrypted data to the remote application which is written in C# because it cannot decrypt using the byte array that has been sent from the Java aplication.
Has anyone come up with a solution that would allow me to tell java not to produce negative numbers when encrypting?
The code is from Micrsoft, the MemoryStream requires the byte[] to create the stream for the crypto code...
As mentioned or not, I replaced byte[] with sbyte but to no avail as MemoryStream requires byte[]
static string DecryptStringFromBytes_Aes(sbyte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("Key");
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an Aes object
// with the specified key and IV.
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream((byte)cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
Java's bytes are signed, C# bytes are unsigned (there's also an sbyte type in C#, that no one uses, which works like Java's bytes).
It doesn't matter. They are different in some regards, namely
when converted to int, C#'s bytes will be zero-extended, Java's bytes will be sign-extended (which is why you almost always see & 0xFF when bytes are used in Java).
when converted to string, Java's bytes will have their 128 - 255 range mapped to -128 - -1. Just ignore that.
The actual value of those bytes (that is, their bit-pattern) is what actually matters, a byte that is 0xAA will be 0xAA regardless of whether you interpret it as 170 (as in C#) or -86 (as in Java). It's the same thing, just a different way to print it as string.
new MemoryStream((byte)cipherText)) definitely doesn't do the right thing (or anything, it shouldn't even compile). The related new MemoryStream((byte[])cipherText)) wouldn't work either, you can't cast between primitive arrays like that. cipherText should just be a byte[] to begin with.
You could turn it into a string with some encoding, like:
encryptedData = encryptCipher.doFinal(strToEncrypt.getBytes());
String s = new String(encryptedData, "Base-64");
Using the same standardized encoding, both C# and Java should be able to reconstruct each others encrypted data from that string.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I use AES to encrypt a 17-digit number (for example, 12345678901234567), then I Base64-encode the byte[] . The length of the final result string is 24. I want to barcode it, the 24 characters are too long. One string which consists of 15 characters would be good, and about 15-digits (for example 123456789012345 ) would be better.
Is there a way to my goal by any
algorithm( AES, DES, 3DES ..)? And how?
My DES encrypt algorithm,
public static byte[] encrypt(byte[] datasource, String password) {
try{
SecureRandom random = new SecureRandom();
DESKeySpec desKey = new DESKeySpec(password.getBytes());
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey securekey = keyFactory.generateSecret(desKey);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
return cipher.doFinal(datasource);
}catch(Throwable e){
e.printStackTrace();
}
return null;
}
private static void longToByteArray(long l, byte[] b) {
b[7] = (byte) (l);
l >>>= 8;
b[6] = (byte) (l);
l >>>= 8;
b[5] = (byte) (l);
l >>>= 8;
b[4] = (byte) (l);
l >>>= 8;
b[3] = (byte) (l);
l >>>= 8;
b[2] = (byte) (l);
l >>>= 8;
b[1] = (byte) (l);
l >>>= 8;
b[0] = (byte) (l);
}
long aliveTime = Long.parseLong("13664547854160806");
byte[] longAsBytes = new byte[8];
longToByteArray(aliveTime, longAsBytes);
byte[] result = DES.encrypt(longAsBytes, password);
String en = REncrypt.base64Encode(result);
An encryption algorithm will not compress data. Indeed, if anything, the encrypted data will be harder to compress than the original. The "scrambling" that the encryption algorithm performs will make any redundancy in the encrypted data harder (probably impossible) to extract.
A better approach would be to compress the 17 digit number before you encrypt it. In this case, converting the decimal characters into a binary number will probably give better compression than a text compression algorithm. A 17 digit number will fit into 8 bytes; i.e. a Java long.
So here's what I would do:
Convert the decimal digits to a long; i.e. parse it using Long.parseLong or similar.
Split the long into 8 bytes, and put them into a byte[].
Encrypt the byte[]. The result should be 8 bytes.
Base64 encode the bytes.
If you do it this way, your encrypted, base64 encoded number should be less than 15 characters.
Note: I'm assuming that you really mean encrypt; i.e. that you need to be able to decrypt and recover the original number.
UPDATE
This probably won't work with many / most decent encryption algorithms because they typically have a minimum block size that is too large. See https://en.wikipedia.org/wiki/Block_size_(cryptography). If you read the article, you will get some insight into why that is so.
You should probably review what you are really trying to achieve here, and decide if encryption is the best way to achieve it.
What is in Java a cipher function for integer encryption having these properties?:
Fast
Symmetric-key algorithm
Simple to use (i.e. a couple of lines of code to use it and no external library to include)
It is possible to specify the output length (e.g. 20 characters)
I need to use it only to encrypt/decrypt integers.
The requirement for no external library reduces the list to DES, 3DES and AES. DES and 3DES have a block size of 64 bits whereas AES has a block size of 128 bits. There are different aspects, one can examine this for.
Ciphertext size
DES and 3DES are best used for integers that are at most 56-bit wide (non-full long), because the result will be a single block of 8 byte, because of padding. If you encrypt a full long value, then an additional padding block will be added.
AES will always produce a 16 byte ciphertext for any int of long value.
Speed
According to this analysis AES (Rijndael-128) is more than twice as fast as DES/3DES with a bigger key size (more secure). AES can be even much faster than DES or 3DES when the CPU supports AES-NI. All current CPUs support this. This is my current result for taken from the openssl speed command.
AES achieves 127MB/s for 16 byte payloads whereas 3DES only achieves 27MB/s. Here's the data to poke around.
Security
Don't use DES for anything serious, because it only has a 56-bit key (64-bit with parity). Brute forcing cost is 256. 3DES is also not that good, because Brute forcing cost is 2112. Brute forcing cost for AES is 2128, 2192, 2256 depending on the used key size.
Code
Probably use AES:
private final String CIPHER_NAME = "AES/ECB/PKCS5Padding";
private final String ALGORITHM_NAME = "AES"; // keySizes 128, 192, 256
// private final String CIPHER_NAME = "DES/ECB/PKCS5Padding";
// private final String ALGORITHM_NAME = "DES"; // keySize 56
// private final String CIPHER_NAME = "DESede/ECB/PKCS5Padding";
// private final String ALGORITHM_NAME = "DESede"; // keySize 168
byte[] encrypt(SecretKey key, long num) {
BigInteger bignum = BigInteger.valueOf(num);
Cipher cipher = Cipher.getInstance(CIPHER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(bignum.toByteArray());
}
long decrypt(SecretKey key, byte[] ct) {
Cipher cipher = Cipher.getInstance(CIPHER_NAME);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] pt = cipher.doFinal(ct);
BigInteger bignum = new BigInteger(pt);
return bignum.longValue();
}
SecretKey keyGen(String algorithm, int keySize) {
KeyGenerator keygen = KeyGenerator.getInstance(algorithm);
keygen.init(keySize);
return keygen.generateKey();
}
Mode of operation
Here I use ECB mode. It is generally not a good idea to use it. It has a problem that encrypting the same plaintext with the same key results in the same ciphertext. This may not be a property that is acceptable. If it is not acceptable, then you need to use for example CBC mode with a new random IV. With will blow up the ciphertext by an additional block.
If you don't need a secure solution, but just fast one, consider the XOR cipher:
int key = ...
....
int b = a ^ key;
int c = b ^ key;
assert (c == a);
You should never implement a cipher yourself if you want any security. There's just too much what can get wrong.
But you can write your numbers into a byte[] and use a cipher provided with Java like described in this answer.
I am having a bit of trouble getting the modulus's size to be consistently 128 bytes big. Sometimes the modulus's byte array has a size of 129 or even 130. I've searched for implementation online, and my implementation is really close to the one from this link: http://introcs.cs.princeton.edu/java/78crypto/RSA.java.html
Here is my implementation:
public static void genKey() throws NoSuchAlgorithmException, NoSuchProviderException {
int bitLength = 512;
SecureRandom srand = new SecureRandom();
BigInteger one = new BigInteger("1");
BigInteger p = BigInteger.probablePrime(bitLength, srand);
BigInteger q = BigInteger.probablePrime(bitLength, srand);
BigInteger phi = (p.subtract(one)).multiply(q.subtract(one));
BigInteger modulus = p.multiply(q); //Varies here
BigInteger publicKey = new BigInteger("65537");
BigInteger privateKey = publicKey.modInverse(phi);
byte[] modulusArray = modulus.toByteArray();
byte[] publicKeyArray = publicKey.toByteArray();
byte[] privateKeyArray = privateKey.toByteArray();
byte[] tmpArray = new byte[128];
for (int i = 0; i < publicKeyArray.length; i++) {
tmpArray[i] = publicKeyArray[i];
}
publicKeyArray = tmpArray;
byte[] publicKeyAndModulus = concat(modulusArray, publicKeyArray);
byte[] privateKeyAndModulus = concat(modulusArray, privateKeyArray);
}
In addition, the privateKey length would vary a bit too. Can I get more consistency with the size using java.Security library or is this not possible to achieve?
The BigInteger#bitLength() function has the necessary documentation:
Returns the number of bits in the minimal two's-complement representation of this BigInteger, excluding a sign bit.
When you generate a BigInteger with bitLength 512, the most significant bit will be 0 ~50% of the time in which case the sign bit will take its place and it will fit into 64 bytes, but in the other cases the most significant bit will be 1 which means that the sign bit will be put into a new byte.
This means that using 511 as the bitLength always results BigIntegers of 64 bytes and 128 bytes for the modulus.
You shouldn't really generate p, q, the modulus and all the other values yourself. It is best to use existing APIs such as Java's Cipher class which also provides proper padding to be used with RSA such as OAEP (PKCS#1 v1.5 is not good anymore): "RSA/ECB/OAEPWithSHA-256AndMGF1Padding".
I suggest you use BouncyCastle and create an AsymmetricCipherKeyPair; here is an example I adapted from RSA using BouncyCastle
public static AsymmetricCipherKeyPair generateKeys(int keySizeInBits) {
RSAKeyPairGenerator kpg = new RSAKeyPairGenerator();
kpg.init(new KeyGenerationParameters(new SecureRandom(), keySizeInBits));
return kpg.generateKeyPair();
}
Hi this is the same question, that was asked two years ago:
Java/JCE: Decrypting “long” message encrypted with RSA
I had a large byte array and rsa keypair, initiated by value 1024.
Using rsa encryption and the specified size of the key is strong requirement, I can't change it. So I can't use symmetric encryption with asymetric encryption symmetric key. I can't use any other keys. I had a byte array and need ciphered byte array to be returned. I wonder if there is any ready tool, that can manage with this problem?
Sorry for such an amateurish question, but I really need a help.
As stated, your question has a single answer, and that's "no". RSA encryption is an algorithm which encrypts messages up to a given size, which depends on the key size; with a 1024-bit RSA key, and RSA as the standard describes it, the maximum size is 117 bytes, no more. There is no way to encrypt a larger message with RSA alone, and that's a definite, mathematical certainty.
If you really need to process longer messages, then you necessarily have to add something else. In that case, please, please, do not try to do anything fancy of your own devising with some oh-so-clever splitting of data into small blocks and the like. That path leads to doom. You might produce something which appears to compile and run, but which will be invariably weak in some way, like almost every other home-made variation on cryptography. That's because security cannot be tested: it is not a case of "works" or "does not work".
The well-trodden path of asymmetric encryption goes thus:
You select a random sequence of bytes of some appropriate length, e.g. 128 bits (that's 16 bytes). Let's call it K.
You encrypt K with the RSA public key; this yields E.
You encrypt the message with K using a symmetric encryption algorithm ("AES/CBC/PKCS5Padding"). Since this is a one-shot key, you can use an all-zeros IV. This yields a bunch of bytes, let's call it F.
The encrypted message is then the concatenation of E and F.
Decryption proceeds in the reverse order: the RSA private key is used to recover K from E, then K is used to decrypt F into the original message. The key K is never stored anywhere, and a new key K is generated every time (even if you encrypt the same message twice). That's important, do not change that unless you understand what you are doing (and if you do, then you already know that).
Given what you state about your problem, you have to do something else than "just RSA". The procedure I describe above is about the best "something else" that you could come up with, security-wise.
Assembling some cryptographic elements into such a protocol is a process fraught with pitfalls so you may have better luck using an already defined format and support library. Two common formats for asymmetric encryption are CMS and OpenPGP. A library which supports both and has good reputation is Bouncy Castle.
If you do need to encrypt/decrypt long strings using RSA, then you can break the bytes up in to smaller "chunks" and process each chunk of bytes through the cipher one at a time while storing the results in a ByteBuffer.
Encryption:
byte[] encData = null;
try {
// create public key
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(key);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pk = kf.generatePublic(publicKeySpec);
Cipher pkCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
pkCipher.init(Cipher.ENCRYPT_MODE, pk);
int chunkSize = 117; // 1024 / 8 - 11(padding) = 117
int encSize = (int) (Math.ceil(data.length/117.0)*128);
int idx = 0;
ByteBuffer buf = ByteBuffer.allocate(encSize);
while (idx < data.length) {
int len = Math.min(data.length-idx, chunkSize);
byte[] encChunk = pkCipher.doFinal(data, idx, len);
buf.put(encChunk);
idx += len;
}
// fully encrypted data
encData = buf.array();
} catch (Exception e) {
e.printStackTrace();
Decryption
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
rsaCipher.init(Cipher.DECRYPT_MODE, rsaPk);
int chunkSize = 128;
int idx = 0;
ByteBuffer buf = ByteBuffer.allocate(data.length);
while(idx < data.length) {
int len = Math.min(data.length-idx, chunkSize);
byte[] chunk = rsaCipher.doFinal(data, idx, len);
buf.put(chunk);
idx += len;
}
// fully decrypted data
byte[] decryptedData = buf.array();