I need to use double length 3DES key to encrypt random 8 bytes and then use the encrypted value to derive a new 3DES key.
When I attempt to instantiate the DESedeKeySpec with the encryptedRandomValue, I get an error message "Wrong key size". Can you advise me how to fix this issue?
DESedeKeySpec myKeySpec = new DESedeKeySpec(encryptedRandomValue);
I can avoid getting this error if all my SecretKeys are single DES key. But I need to use double length 3DES key and ECB mode.
Here is my code;
// Generate double length 3DES Master Key
KeyGenerator masterEncKeyGenerator = KeyGenerator.getInstance("DESede");
masterEncKeyGenerator.init(112);
SecretKey masterKey = masterEncKeyGenerator.generateKey();
//Prepare random bytes
byte[] randomKeyValue = "rn4yrbdy".getBytes();
// Encrypt random bytes with the 3DES Master key
final Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, masterKey);
byte[] encryptedRandomValue = cipher.doFinal(randomKeyValue);
// Derive new key 3DES Key
SecretKeyFactory mySecretKeyFactory = SecretKeyFactory.getInstance("DESede");
DESedeKeySpec myKeySpec = new DESedeKeySpec(encryptedRandomValue);
SecretKey derivedKey = mySecretKeyFactory.generateSecret(myKeySpec);
I realize why I am getting this error. It is because the DESedeKeySpec needs to take in 24 bytes as key material but I am giving it only 8. But that's the requirement I have: to produce a 3DES key out of encrypted, with master 3DES key, random 8 bytes data...
Triple DES is just 3 consecutive single DES operation done with different keys. Specifically it's a DES encrypt followed by a DES decrypt followed by a DES encrypt.
DES encrypt-decrypt-encrypt is what gives it the name DESede
The difference betwheen double length and tripple length keys is what part of the key you use in each of the three DES operation.
A double length key: k1 || k2 would give the following DES operations:
Encrypt(k1) - Decrypt(k2) - Encrypt(k1)
A tripple length key: k1 || k2 || k3 would give the following DES operations:
Encrypt(k1) - Decrypt(k2) - Encrypt(k3)
The standard implementation in Java don't support double length triple DES keys directly but you can get the same effect by repeating the first part of the key as the third part: k1 || k2 || k1
As a curiosity you can support single DES encryption via tripple DES by repeating the single DES key three times like: k1 || k1 || k1. This simplifies backword compatibility in e.g. hardware implementations.
Related
I have a 2048 bit RSAPrivateKey in PKCS#8 format. I would like to flip single bits of the private exponent so that bruteforce becomes necessary.
This is how I create the key pair:
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
In order to flip bits I convert the key into a Java.util.BitSet:
BitSet priv = BitSet.valueOf(keyPair.getPrivate().getEncoded());
The outcoming BitSet has got priv.size() = 9792 Bits (sometimes it differs a little bit). I need to know which of the containing bits (probably 2048) represent the private secret. I've tried to find out its structure by looking at RFC5208 but I can't find any bit related information. I've also tried to compare the private exponent rsaPrivateKey.getPrivateExponent() with the entire private key new BigInteger(rsaPrivateKey.getEncoded()) but could not find any similarities.
However, as I start flipping random bits either the decryption works anyhow or I get BadPaddingExceptions. To my way of thinking decryption might work with flipped bits but results in wrong decrypted plaintexts. Which bits do I have to flip to achieve this? Any suggestions are most welcome.
I gather what you want to do is take an existing RSA private key, mess around with the private exponent, and take the result and make it back into an RSA private key for further experiments. I would not use the getEncoded() method because the encoded object is the DER encoding of complex ASN.1 structure and you don't want to parse that to find your way to the private exponent. A better way is to extract the private exponent directly as a BigInteger.
Here is one example of how to do this to flip the 29th bit. Obviously you can tailor this to meet your needs.
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
BigInteger d = rsaPrivateKey.getPrivateExponent();
// Flip some bits in d
BigInteger newD = d.flipBit(29);
// Make a new RSA private key with the modified private exponent
RSAPrivateKeySpec newRSAPrivateSpec = new RSAPrivateKeySpec(rsaPrivateKey.getModulus(), newD);
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPrivateKey newPrivateKey = (RSAPrivateKey) kf.generatePrivate(newRSAPrivateSpec);
One thing to keep in mind: when you modify just the private exponent, the public exponent is no longer correct -- in fact the private exponent may also be invalid. If you want a valid RSA keypair you'll have to verify that the new private exponent d satisfies GCD(d, phi(n)) = 1, where phi(n) = (p-1) * (q-1) and p and q are the two prime factors of your RSA modulus, and then compute the new public exponent e=d-1 mod phi(n).
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.
how do i cast a Big Integer into a Key for java cryptography library?
I am trying to use a shared diffie hellman key that i generated myself for the key value for AES encryption.
Below is the code that i used
BigInteger bi; long value = 1000000000;
bi = BigInteger.valueOf(value);
Key key = new Key (bi);
however it did not work.
May i know how do i convert a BigInteger value into a Key value?
Thanks in advance!
First, you cannot cast it. There is no relationship between the BigInteger class and the Key interface.
Second, Key is an interface not a class, so you can't create instances of it. What you need to create is an instance of some class that implements Key. And it most likely needs to be a specific implementation class, not (say) an anonymous class.
The final thing is that the Java crypto APIs are designed to hide the representation of the key. To create a key from bytes, you need to create a KeySpec object; e.g. SecretKeySpec(byte[] key, String algorithm)) and then use a KeyFactory to "generate" a key from it. Typical KeySpec constructors take a byte[] as a parameter, so you first need to get the byte array from your BigInteger instance.
You need to convert your BigInteger to a byte array of a specific size, then use the first (leftmost) bytes to create a key. For this you need to know the size of the prime p used in DH, as the value needs to be left-padded to represent a key. I would suggest to use standardized DH parameters (or at least make sure that the size of the prime is dividable by 8).
Note that there may be a zero valued byte in front of the byte array retrieved using BigInteger.toByteArray() because the value returned is encoded as a signed (two-complement) big-endian byte array. You need to remove this byte if the result is bigger than the prime (in bytes) because of it.
public static byte[] encodeSharedSecret(final BigInteger sharedSecret, final int primeSizeBits) {
// TODO assignment add additional tests on input
final int sharedSecretSize = (primeSizeBits + Byte.SIZE - 1) / Byte.SIZE;
final byte[] signedSharedSecretEncoding = sharedSecret.toByteArray();
final int signedSharedSecretEncodingLength = signedSharedSecretEncoding.length;
if (signedSharedSecretEncodingLength == sharedSecretSize) {
return signedSharedSecretEncoding;
}
if (signedSharedSecretEncodingLength == sharedSecretSize + 1) {
final byte[] sharedSecretEncoding = new byte[sharedSecretSize];
System.arraycopy(signedSharedSecretEncoding, 1, sharedSecretEncoding, 0, sharedSecretSize);
return sharedSecretEncoding;
}
if (signedSharedSecretEncodingLength < sharedSecretSize) {
final byte[] sharedSecretEncoding = new byte[sharedSecretSize];
System.arraycopy(signedSharedSecretEncoding, 0,
sharedSecretEncoding, sharedSecretSize - signedSharedSecretEncodingLength, signedSharedSecretEncodingLength);
return sharedSecretEncoding;
}
throw new IllegalArgumentException("Shared secret is too big");
}
After that you need to derive the key bytes using some kind of key derivation scheme. The one you should use depends on the standard you are implementing:
As stated in RFC 2631
X9.42 provides an algorithm for generating an essentially arbitrary
amount of keying material from ZZ. Our algorithm is derived from that
algorithm by mandating some optional fields and omitting others.
KM = H ( ZZ || OtherInfo)
H is the message digest function SHA-1 [FIPS-180] ZZ is the shared
secret value computed in Section 2.1.1. Leading zeros MUST be
preserved, so that ZZ occupies as many octets as p.
Note that I have discovered a bug in the Bouncy Castle libraries up to 1.49 (that's the current version at this date) in the DH implementation regarding the secret extraction - it does strip the spurious leading 00h valued bytes, but it forgets to left-pad the result up to the prime size p. This will lead to an incorrect derived key once in 192 times (!)
I'm making a system where I want to verify the server's identity via RSA, but I can't seem to get the server to properly decrypt the client's message.
The public and private keys are in slot 0 of the array, and mod is in slot 1, so they are setup correctly.
Client side code
int keyLength = 3072 / 8;//RSA key size
byte[] data = new byte[keyLength];
//Generate some random data. Note that
//Only the fist half of this will be used.
new SecureRandom().nextBytes(data);
int serverKeySize = in.readInt();
if (serverKeySize != keyLength) {//Definitely not the right heard
return false;
}
//Take the server's half of the random data and pass ours
in.readFully(data, keyLength / 2 , keyLength / 2);
//Encrypt the data
BigInteger[] keys = getKeys();
BigInteger original = new BigInteger(data);
BigInteger encrypted = original.modPow(keys[0], keys[1]);
data = encrypted.toByteArray();
out.write(data);
//If the server's hash doesn't match, the server has the wrong key!
in.readFully(data, 0, data.length);
BigInteger decrypted = new BigInteger(data);
return original.equals(decrypted);
Server side code
int keyLength = 3072 / 8;//Key length
byte[] data = new byte[keyLength];
//Send the second half of the key
out.write(data, keyLength / 2, keyLength / 2);
in.readFully(data);
BigInteger[] keys = getKeys();
BigInteger encrypted = new BigInteger(data);
BigInteger original = encrypted.modPow(keys[0], keys[1]);
data = original.toByteArray();
out.write(data);
AFAIK that implementation is correct however it doesn't seem to produce the correct output. Also no, I do not wish to use a Cipher for various reasons.
There are some critical details that are not being accounted for. The data you want to apply RSA to must be encoded as BigInteger x, with 0 <= x < n, where n is your modulus. You aren't doing that. In fact, because you are filling your entire data array with random data you cannot guarantee that. The PKCS#1 padding algorithm is designed to do this correctly, but since you are rolling your own you'll have to fix this in your code. Also, examine carefully how the BigInteger(byte[]) constructor and BigInteger.toByteArray() decode/encode integers. Naively many expect simply the base 256 encoding, and forget that BigInteger must accommodate negative integer also. It does so by using the ASN.1 DER integer rules. If the positive integer's high-order byte would be >= 128 then a leading zero byte is added.
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();