I got the following method body to generate a key for encryption:
new BigInteger(500, new SecureRandom()).toString(64);
Could anyone explain what is the size of generated key?
It's a secure random number with a length of 500 bit in your case. Have a look at the javadoc of the BigInteger(int numBits, Random rnd) constructor.
Your line of code creates a 500 bit integer and apparently tries to convert it to a String in Base64 - that's the toString() call. But that won't work, because BigInteger.toString() only works up to base 36 and defaults to decimal otherwise. If you need a Base64 representation, you have to use a third-party class, as there is AFAIK no Base64 encoder in the standard API.
Normally you would want your encryption key to be a power of 2. So perhaps you mean 512 bits?
First, as other suggested, you will get IllegalArgumentException because BigInteger doesn't support radix 64.
Even if you use a valid radix, the number of characters generated varies because BigInteger strips leading 0s and you might also get minus sign in the string.
To get random keys, just use random bytes directly. Say you want 128-bit (16 bytes) AES key, just do this,
byte[] keyBytes = new byte[16];
new SecureRandom().nextBytes(keyBytes);
SecretKey aesKey = new SecretKeySpec(keyBytes, "AES");
Related
How would I go about doing that? I tried using SHA-1 and MD5 but the output is too long for my requirements and truncation would not make it unique.
Input : String containing numbers e.g. (0302160123456789)
Received output : 30f2bddc3e2fba9c05d97d04f8da4449
Desired Output: Unique number within range (0000000000000000 - FFFFFFFFFFFFFFFF) and 16 characters long
Any help/ pointers are greatly appreciated.
How big is your input domain? If it is bigger than your output domain, then the Pigeon Hole principle applies and you can't get unique output by definition.
If the input domain is smaller or equal to the output domain, then you can easily accomplish this with a Pseudo-Random Permutation (PRP) that block ciphers provide.
The output of 16 hexits is equivalent to 8 bytes and equivalent to 64 bit. DES (and Triple DES) is a block cipher that has this block size.
Parse the input string to a byte array in a compact fashion. If the input always consists of numerical digits, you can use Ebbe M. Pedersen's approach with
byte[] plaintext = new BigInteger("0302160123456789").toByteArray();
Then you can generate some random, but fixed key of 24 bytes for Triple DES and instantiate the cipher with:
Cipher c = Cipher.getInstance("DESede/ECB/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "DESede"));
byte[] ciphertext = c.doFinal(plaintext);
Use some kind of Hex converter to get the representation you want.
You can "hash" numbers up to 36028797018963968 with this. If you want larger numbers (up to 9223372036854775808), then you need to use "DESede/ECB/NoPadding" and pad yourself with some padding bytes.
are you going to receive more than FFFFFFFFFFFFFFFF different strings?
if not then it's a simple problem of generating integers: the first string will get 0 the next 1 etc; you just keep a list of the strings and check if something the same appears.
You could just convert your number to hex using BigInteger like this:
String id = new BigInteger("0302160123456789").toString(16);
System.out.println(id);
That gives:
112d022d2ed15
I need to generated random 32 characters string with the use of SecureRandom class. I tried with generating 32 byte array then use Base64 encoding:
byte[] bytes = new byte[32];
new SecureRandom().nextBytes(bytes);
new String(Base64.encodeBase64(bytes));
But this code generate a string with more than 32 characters. How can I get random 32 characters while still using SecureRandom class ?
Try to encode 22 to 24 bytes instead.
When encoding this amount, the resulting Base64 encoded string should contain exactly 32 characters although some of them might be = marks based on whether its 22 or 23 bytes due to padding.
If you don't want the = marks, just encode 24 bytes and no padding will be added.
If you are more interested on how the padding or Base64 encoding works, the current wikipedia article is quite detailed.
e.g. change your code accordingly:
byte[] bytes = new byte[24];
new SecureRandom().nextBytes(bytes);
new String(Base64.encodeBase64(bytes)); // Should be 32 characters in length.
We can achieve this using single line of code. Use org.apache.commons.lang.RandomStringUtilsfrom commons-lang library.
Code :
RandomStringUtils.randomAlphabetic(32);
I have written a method to convert a plain text into it's hashcode using MD5 algorithm. Please find the code below which I used.
public static String convertToMD5Hash(final String plainText){
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
LOGGER.warn("For some wierd reason the MD5 algorithm was not found.", e);
}
messageDigest.reset();
messageDigest.update(plainText.getBytes());
final byte[] digest = messageDigest.digest();
final BigInteger bigInt = new BigInteger(1, digest);
String hashtext = bigInt.toString(8);
return hashtext;
}
This method works perfectly but it returns a lengthy hash. I need to limit this hash text to 8 characters. Is there any possibilities to set the length of the hashcodes in Java?
Yes and No. You can use a substring of the original hash if you always cut the original hash-string similary (ie. 8 last/first characters). What are you going to do with that "semi-hash" is another thing.
Whatever it is you're going to do, be sure it has nothing to do with security.
Here's why: MD5 is 128-bit hash, so there's 2^128 = ~340,000,000,000,000,000,000,000,000,000,000,000,000 possible permutations. The quite astronomical amount of permutations is the thing that makes bruteforcing this kind of string virtually impossible. By cutting down to 8 characters, you'll end up with 32-bit hash. This is because a single hex-value takes 4 bits to represent (thus, also 128-bit / 4 bit = 32 hex-values). With 32-bit hash there's only 2^32 = 4,294,967,296 combinations. That's about 79,228,162,514,264,337,593,543,950,336 times less secure than original 128-bit hash and can be broken in matter of seconds with any old computer that has processing power of an 80's calculator.
No. MD5 is defined to return 128 bit values. You could use Base64 to encode them to ASCII and truncate it using String#substring(0, 8).
In Java 8 (not officially released yet), you can encode a byte[] to Base64 as follows:
String base64 = Base64.getEncoder().encodeToString(digest);
For earlier Java versions see Decode Base64 data in Java
all hash algorithms should randomly change bits in whole hash whenever any part of data has changed. so you can just choose 8 chars from your hash. just don't pick them randomly - it must be reproducible
Firstly as everyone has mentioned, the 64 bit hash is not secure enough. Ultimately it depends on what you exactly plan to do with the hash.
If you still need to convert this to 8 characters, I suggest downcasting the BigInteger to a Long value using BigIteger.longValue()
It will ensure that the long value it produces is consistent with the hash that was produced.
I am not sure if taking most significant 64 bits from the 128 bit hash is good idea. I would rather take least significant 64 bits. What this ensures is that
when hash(128, a) = hash(128, b) then hash(64, a) = hash(64, b) will always be true.
But we have to live with collision in case of 64 bits i.e. when hash(64, a) = hash(64, b) then hash(128, a) = hash(128, b) is not always true.
In a nutshell, we ensure that we do not have a case where 128 bit hashes of 2 texts are different, but their 64 bit hashes are same. It depends on what you really use the hash for, but I personally feel this approach is more correct.
I have been given a key as a string and an encrypted file using DES. That is all I know. I don't know how the key was encoded.
There is also a des.exe that I can use to decrypt, this is all I found on the Internet: http://knowledge-republic.com/CRM/2011/07/how-to-decrypt-extract-recreate-thecus-storage-firmware/
Using des.exe, the only command it works with is "-D", not "-d".
My goal is to use Java to do the same thing. I copied and pasted this from somewhere
String key = "blah";
DESKeySpec dks = new DESKeySpec(key.getBytes());
SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
SecretKey desKey = skf.generateSecret(dks);
System.out.println(desKey);
Cipher cipher = Cipher.getInstance("DES"); // DES/ECB/PKCS5Padding for SunJCE
if (mode == Cipher.DECRYPT_MODE) {
cipher.init(Cipher.DECRYPT_MODE, desKey);
CipherOutputStream cos = new CipherOutputStream(os, cipher);
doCopy(is, cos);
}
and it doesn't work.
What are some other options in converting a string into a key?
Should probably add I'm a complete newb at cryptography.
The SunOS man page for des (which seems to be what your des.exe is based on?) indicates that they key is generated like this:
The DES algorithm requires an 8 byte key whose low order bits are assumed to be odd-parity bits. The ASCII key supplied by the user is zero padded to 8 bytes and the high order bits are set to be odd-parity bits. The DES algorithm then ignores the low bit of each ASCII character, but that bit's information has been preserved in the high bit due to the parity.
It also mentions that the initial IV is always zero'd out, no matter what mode you are running in
The CBC mode of operation always uses an initial value of all zeros
for the initialization vector, so the first 8 bytes of a file are
encrypted the same whether in CBC or ECB mode.
It also mentions that the padding used is such that the last byte is always a value from 0-7, indicating the number of padding bytes used. This is similar to PKCS5Padding, so perhaps that would work
Since the CBC and ECB modes of DES require units of 8 bytes to be
encrypted, files being encrypted by the des command have 1 to 8 bytes
appended to them to cause them to be a multiple of 8 bytes. The last
byte, when decrypted, gives the number of bytes (0 to 7) which are to
be saved of the last 8 bytes. The other bytes of those appended to the
input are randomized before encryption.
Based on the options you indicated you are using, it sounds like you are using DES/CBC/PKCS5Padding for the cipher.
I think that just leaves determining how to actually derive the key. I found this sample code on exampledepot which might work for you. I think you would just need to convert your string password into 8 bytes (1 byte per character, so no UTF encodings) then stuff it through the code in the example to derive the key. Its worth a shot anyway.
DES keys are 7 (apparently SunJCE uses 7?) or 8 bytes. Check if the string you have been provided is 7 or 8 bytes. If so, then the chances are good it's the raw key. If not, it could be encoded in some fashion. A giveaway for hex encoding would be a prefix of 0x or suffix of h, and all characters would be in the range 0-9,A-F. You can certainly convert from hex yourself or use some code on the web, but I usually use an Apache commons lib (http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Hex.html).
That said, this is really speculation and I'm not sure we can jump to the conclusion that it's a problem with the key alone. Do you have any other info on the purported encryption algorithm? If the executable you cited works with "-d" then it seems like the encryption is plain DES in CBC mode:
-b : encrypt using DES in ecb encryption mode, the defaut is cbc mode.
(there are multiple possible modes, see http://download.oracle.com/javase/1.4.2/docs/guide/security/jce/JCERefGuide.html#AppA)
I would try setting your cipher to "DES/CBC".
Then again, I'm not sure how to interpret this:
Default is tripple cbc
You may be able to use this snippet to tell what ciphers are available on your system: http://www.java2s.com/Code/Java/Security/ListAllProviderAndItsAlgorithms.htm
I had the same issue with C#. I solved it in the end. You can have a look at my answer here: DES Initialization Vector in C#
Generally, what des.exe does, is that it computes a checksum using DES. So each encryption step is using the previous result instead of advancing in the output array.
I want to encrypt an integer with DES, the resultant cipher text should also be an integer.
Decryption function should follow this notion as well.
I am trying to modifying the code at Encrypting a String with DES, by converting the byte array to integer, instead of using Base64 encoding. However the decryption function throws an exception of improper padding, as the conversion of integer to byte[] results in a 4 byte array.
Is there any other encryption algorithm that I can use to achieve this.
I am not concerned about the weakness of the cipher text.
If you are running an Integer value through DES to produce another Integer value, and you don't care about the cipher text weakness, then you are merely doing a very expensive hashing operation. You'd be better off generating a random integer as the key and bitwise xor-ing the exponent and the random integer. This would take nanoseconds to compute and have exactly the same security.
DES has a 64 bit blocksize, so in general the output from the encryption of a 32 bit int will be a 64 bit block. It will be easier to encrypt a 64 bit long to another 64 bit long. Use ECB mode so that padding is not an issue, or at least you are adding zero bits to the front of your int to extend it to 64 bits.
If you merely want to smush up your int then Jim's suggestion is excellent.
You want to look at Format Perserving Encryption. There are a couple of techniques for it, but in general all of them will generate a value in the same domain as your input ( i.e. integers, credit card numbers,etc)