I want that encrypted data have the same length as my original text, because I must respect the constraint of length. I am using BouncyCastle.
Here's my code to generate the keys pair:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(512);
KeyPair keypair = keyGen.genKeyPair();
PublicKey pub = keypair.getPublic();
byte[] pubs = pub.getEncoded();
And here's my code to encrypt:
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
PublicKey pk = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(pubs));
cipher.init(Cipher.ENCRYPT_MODE, pk);
byte[] cipherBytes = cipher.doFinal(plainArray);
The encoded data is very large, what can I do to make as small as original data ?
Nothing, at least not regarding the RSA. RSA requires a certain amount of padding to be secure, and as the data will be seen as random data by any compression method, you cannot compress it either.
Of course, you should not directly encrypt data directly using RSA, you should encrypt a random session/data key instead. But even then, you will have the encrypted session key as overhead.
You can remove some bits for the asymmetric encrypted data/key by using Elliptic Curve Cryptography (the output of which is double the key size minimum, but key sizes are much smaller to achieve the same level of security). EC cryptography is not for the meek though, it has lots of complexity.
512 bit RSA is considered insecure by the way. Use 1024 as minimum or keep to the ECRYPT II or NIST recommendations listed at http://www.keylength.com/ .
If you can keep the key secret, you can use a symmetric cryptosystem like AES. If used in CFB mode, it can be adapted to any bit length. I.e. #bits input & output is identical.
RSA and ElGamal -- the main public-key cryptosystems-- can be broken in a matter of hours even at 512 bits as you are selecting. 1024 - 4096 bits is normal. Less than 512 bits is worthless unless your opponent is limited to using 1990s hardware. :-)
The nature of RSA is such that you use exponentiation in encryption and decryption.
Basically, for public key e and private key d, you take a message m and then you get a compressed message equal to c=m^e and mod it by n. Then you decrypt with m=c^d and again mod it by n (mod means take the remainder).
If you think about it, a message to the power of something results in a larger number. Because you do a mod n, you end up with a number that is at most n-1. n is the key length.
So basically, whatever message you take, you encrypt to something as large as n. The message has to be less than n.
But the message has to be turned into an integer using a padding scheme (so you can do the exponentiation). This padding scheme may require less than n bits. And so, you end up with larger files than the size of the data being encrypted. Also, the last chunk may be less than n bits, but will encrypt into size n.
Related
Given an arbitrary Java byte array for example 1024 byte array I would like to derive an AES-256 bit key. The array is generated from ECHD via javax.crypto.KeyAgreement using byte[] secret = keyAgreement.generateSecret()
My current solution is to treat the input byte array as a password. Use the PBKDF2 key derivation function the input array as both the password and the salt as shown below.
UPDATE: I have set UTF-8 as the encoding to address issues pointed out in comments and answers.
private byte[] deriveAes256bitKey(byte[] secret)
throws NoSuchAlgorithmException, InvalidKeySpecException {
var secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
var password = new String(secret, UTF_8).toCharArray();
var keySpec = new PBEKeySpec(password, secret, 1024, 256);
return secretKeyFactory.generateSecret(keySpec).getEncoded();
}
Is there a better way to take a byte array in Java and turn it into an AES-256 bit key?
I would be wary of using new String(input).toCharArray() to create the password. It's not portable (it uses the platform default encoding), and its behaviour is undefined if there are invalid character sequences in the input.
Consider this:
System.out.println(new String(new byte[] {(byte) 0xf0, (byte) 0x82, (byte) 0x82, (byte) 0xac}, StandardCharsets.UTF_8));
f08282ac is an over long encoding of the Euro sign (€). It's decoded to the replacement character (�; 0xfffd) because it's an illegal sequence. All illegal UTF-8 sequences will end up as the replacement char, which is not what you want.
You could avoid decoding problems by serialising the byte array before passing it to the SecretKeyFactory (base64 encode it, or simply new BigInteger(input).toString(Character.MAX_RADIX)). However, this can be avoided if you don't use the SecretKeyFactory. It's unnecessary.
PBKDF2 (Password-Based Key Derivation Function 2) is designed to make brute force attacks against user supplied passwords harder by being computationally expensive and adding salt.
You don't need that here (your input is large and random; nobody will be mounting dictionary attacks against it). Your problem is just that the input length doesn't match the required key length.
You can just hash the input to the correct length:
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] keyBytes = md.digest(input);
What is required here is a KBKDF or Key Based Key Derivation Function. A KBKDF converts a secret value that contains enough entropy into a different key of a specific size. A PBKDF is used when you have a passphrase with potentially too little entropy into a key using key strenghtening (using the salt and work factor or iteration count). The work factor / iteration count doesn't need to be used if the input value is already strong enough not to be guessed / brute forced.
SHA-256 in general suffices if you only want a resulting 128 bit value. However, using a key derivation function may still offer benefits. First of all, it is a function that is explicitly defined for the function, so it is easier to prove that it is secure. Furthermore, it is generally possible to add additional data to the key derivation function so that you can e.g. derive more keys or a key and an IV. Or you can expand the configurable output size to output enough data for different keys or key / IV.
That said, most cryptographers won't frown too much if you use SHA-256 (or SHA-512 in case you require more bits for key / IV). The output is still supposed to be randomized using all possible bits from the input, and it is impossible to inverse the function.
I'm dealing with a legacy application that uses a custom protocol to cipher communication. Random AES keys are generated in legacy Java app like this:
keygen = KeyGenerator.getInstance("AES");
keygen.init(128);
keygen.generateKey().getEncoded();
I've been looking for solutions on crypto with no luck. How can I generate this key on nodejs?
That code probably does not do as much as you think. It simply generates 16 (128 / 8) secure random bytes, then wraps a key object around it. So with nodejs, you simply generate 16 bytes and feed the algorithm the raw key data.
If you want to use the generated key, then make sure you create a binary encoded string or buffer from the bytes returned by the getEncoded() method. You could use hexadecimal encoding/decoding if you require the key to be a textual string somewhere in the process.
See randomBytes() and createCipheriv() for information.
AES keys are just cryptographically strong random bytes, DES (parity bits) and RSA (prime number calculation) keys are not.
I'm working on a Java authentication subsystem that specs the storage of passwords in the DB as PBKDF2-generated hashes, and I'm now trying to decide whether I should use SHA1 or SHA512 as PRF. I went through the specs of both but they are very mathematically intensive for me to follow. Can somebody with better crypto-understanding explain how PBKDF2WithHmacSHA512 differs from PBKDF2WithHmacSHA1?
Here's what I'm trying to do:
private static final int HASH_BYTE_SIZE = 64 * 8; // 512 bits
private static final int PBKDF2_ITERATIONS = 1000;
// generate random salt
SecureRandom random = new SecureRandom();
byte salt[] = new byte[SALT_BYTE_SIZE]; // use salt size at least as long as hash
random.nextBytes(salt);
// generate Hash
PBEKeySpec spec = new PBEKeySpec(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE);
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); // we would like this to be "PBKDF2WithHmacSHA512" instead? What Provider implements it?
byte[] hash = skf.generateSecret(spec).getEncoded();
// convert hash and salt to hex and store in DB as CHAR(64)...
Let us breakdown the word piece by piece:
PBKDF2--WithHmac--SHA512
Let's go over it part by part
PBKDF2
Stands for Password-based-Key-Derivative-Function, a successor of PBKDF1 and is used to implement a pseudorandom function, such as a cryptographic hash, cipher, or HMAC to the input password or passphrase along with a salt value and repeats the process many times to produce a derived key, which can then be used as a cryptographic key in subsequent operations.
HMAC
Stands for Keyed-Hash Message Authentication Code (HMAC) is a specific construction for calculating a message authentication code (MAC) involving a cryptographic hash function in combination with a secret cryptographic key. Any cryptographic hash function,may be used in the calculation of an HMAC; the resulting MAC algorithm is termed HMAC-MD5 or HMAC-SHA1 accordingly.
SHA512
Well, you know about it.. :P
Now, coming back to your question, the line of code:
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
specifies the keyFactory to use the algorithm PDBKDF2WithHmacSHA1. When you would do something like:
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
you tell the factory to use algorithm PBDKF2WithHmacSHA512.
Essentialy the main difference between PBKDF2WithHmacSHA1 and PBKDF2WithHmacSHA512 is that:
The PBKDF2WithHmacSHA1 will produce a hash length of 160 bits.
The PBKDF2WithHmacSHA512 will produce a hash length of 512 bits.
Hence the latter is more secure. But there are arguments on both sides as to which is sufficient for encryption. No debate. Just saying.
Some additional information about the two algorithms:
HMACSHA1
HMACSHA1 is a type of keyed hash algorithm that is constructed from the SHA1 hash function and used as an HMAC, or hash-based message
authentication code. The HMAC process mixes a secret key with the
message data, hashes the result with the hash function, mixes that
hash value with the secret key again, and then applies the hash
function a second time. The output hash is 160 bits in length.
HMACSHA512
HMACSHA512 is a type of keyed hash algorithm that is constructed from
the SHA-512 hash function and used as a Hash-based Message
Authentication Code (HMAC). The HMAC process mixes a secret key with
the message data and hashes the result. The hash value is mixed with
the secret key again, and then hashed a second time. The output hash
is 512 bits in length.
The main advantage is that HmacWith512 is more secure than HmacWith256. For e.g.
HMAC_SHA1("key","The quick brown fox jumps over the lazy dog") = 0xde7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9
HMAC_SHA512("key","The quick brown fox jumps over the lazy dog") = 0xb42af09057bac1e2d41708e48a902e09b5ff7f12ab428a4fe86653c73dd248fb82f948a549f7b791a5b41915ee4d1ec3935357e4e2317250d0372afa2ebeeb3a
The difference is pretty huge (as seen). Hope it helps. :)
EDIT: As OP mentions
PBEKeySpec(char[] password, byte[] salt, int iterationCount, int keyLength)
The parameter keyLength is used to indicate the preference on key length
for variable-key-size ciphers. The actual key size depends on each provider's implementation. Hence say, doing something like
PBEKeySpec(password, salt, int 100, 512) doesn't mean you will be using SHA1 to generate a keyLength of 512. It simply means that. SHA1 supports upto a maximum of 160 bits. You cannot exceed that.
As for your second question, have a look at the HMAC-SHA1. There are many statements that say algorithms like SHA256 is pretty good if you a long hash.
Also, as per the NSA :
The NSA specifies that "Elliptic Curve Public Key Cryptography using
the 256-bit prime modulus elliptic curve as specified in FIPS-186-2
and SHA-256 are appropriate for protecting classified information up
to the SECRET level. Use of the 384-bit prime modulus elliptic curve
and SHA-384 are necessary for the protection of TOP SECRET
information.
I think using a HMAC function in conjunction SHA512 is quite secure.
SHA512 belongs to the SHA2 family of cryptographic hash functions. Since SHA1 has theoretical weaknesses and SHA512 is slightly slower than SHA1 (slower is better when hashing passwords), SHA512 (or any of the SHA2 family) should be chosen over SHA1 for the purpose of hashing passwords.
Actually understanding the differences in the functions is not going to be simple, but you might have a better chance of getting an answer on the Crypto SE site.
I have to create a signature from given data, but the problem is that data has length as key length. All implementation of RSA Signers in JAVA allow to put data of max. key length minus one length. How to create signature for data with length as key length? Algorithm is basic RSA without padding and without digest.
Firstly, please don't use RSA without padding - it's a terrible idea and diminishes the security of your system.
If you need to sign data that is equal in size to your key length (or larger), I'm afraid you'll need to use a digest. There's not really a choice here. I'm not sure why you wouldn't want to use a digest anyway.
RSA is only able to encrypt data to a maximum amount of your key size (2048 bits = 256 bytes) minus padding / header data (11 bytes for PKCS#1 v1.5 padding).
As a result it is often not possible to encrypt files with RSA directly. RSA is also not meant for this purpose. If you want to encrypt more data, you can use something like:
Generate a 256-bit random keystring K
Encrypt your data with AES-CBC with K
Encrypt K with RSA
Send both to the other side
I'm currently working on a protocol, which uses Diffie-Hellman for an key exchange.
I receive a packet, which consists of an aes-128 encrypted part and a 128 Bit DH Public Key.
In a very last step in the protocol, the aes key is sent to another peer in the network.
This aes-key should be encrypted with a cipher using a 128 bit strong secretkey.
I plan to use Blowfish (can also be another cipher, doesn't really matter for the problem)
Now to encrypt the aes-key, with lets say blowfish, I have to build a secretkey for the encryption with a class called SecretKeySpec (I'm using javax.crypto stuff), which takes an byteArray in the constructor to build the secretKey.
The sharedkey from DH is a 128 Bit BigInteger.
Well, now I can interpret my shared-key as a byteArray (wich gives me still 128 Bit in 16Bytes [where the numbers are interpreted as frames of 8 Bit data])
So my question is, how strong is my key really?
Is there any impact because I only use numbers as input for the byteArray (so does this limit the keyspace in any way?)
I think this is not the case, but I'm not 100% sure.
Maybe someone can do the math and proof me right or wrong.
If I'm wrong what keysize for the shared key give me piece of mind to finally get to the 128Bit SecretKey for the encryption?
The Crypto++ website suggests using a minimum p of 3072 bits (or 256 bits for an ECC implementation) to transport a 128 bit AES key.
You might wish to study the references provided at http://www.keylength.com/en/compare/ for further information about comparing key lengths among different algorithms.
Not an expert in DH here, but to me it seems that DH's keyspace for the shared key represented in n bits is somewhat smaller than 2^n.