In order to access resources in my web service application, client applications need to provide a shared secret unique for each resource.
Question is: Are Longs generated by a SecureRandom reasonably safe for this purpose (e.g. against brute force attacks)? Or should I use a UUID instead?
The web service runs over HTTPS and I can guarantee that no collision (with either Long or UUID) occurs. Question really only is whether the domain of a Long in Java is large enough to provide protection against exhaustive attacks over HTTPS.
The simple answer is that you can't EVER guarantee there won't be any collisions for a randomly generated number pair / sequence. All you can do is design things so that the probability of a collision is acceptably low for the application. How low that probability needs to be depends on the details of the application.
The thing that puzzles me about this is why a collision would be a problem with shared secrets at all. Are you really asking about the probability of someone guessing the shared secret?
OK, so this is a simple maths problem. Take the long example.
There are 2^64 possible values of a long.
About V of these are "valid secrets".
Some bad guy could plausibly try out N guessed secrets per second.
You can derive a formula for the probability P that someone can guess one of your secrets in a given time interval T.
Derive the formula, plug in the variables V, N and T, and decide whether P is acceptable.
Note that "a practically relevant chance" is not something we can advise you on. Rather you should be deciding what an acceptable risk ... based on an analysis of the costs / consequences of someone succeeding in breaking your scheme.
Perhaps, but why use them when the JCE supports this very function?
DHParameterSpec dhSkipParamSpec = new DHParameterSpec(skip1024Modulus, skip1024Base);
KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH", "BC");
aliceKpairGen.initialize(dhSkipParamSpec);
KeyPair aliceKpair = aliceKpairGen.generateKeyPair();
byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();
aliceKeyAgree = KeyAgreement.getInstance("DH", "BC");
aliceKeyAgree.init(aliceKpair.getPrivate());
//... obtaining Bob's Public Key
aliceKeyFac = KeyFactory.getInstance("DH", "BC");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(bobPubKeyEnc);
bobPubKey = aliceKeyFac.generatePublic(x509KeySpec);
aliceKeyAgree.doPhase(bobPubKey, true);
SecretKey aliceAesKey = aliceKeyAgree.generateSecret("AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, aliceAesKey);
byte[] cipherText = cipher.doFinal(plaintext.getBytes());
And your AES-encrypted text is in cipherText now. Hope that helped...
Related
I have encrypted the string in PHP using AES-256-ECB.
$sString = "test"
$sEncryptionMethod = "AES-256-ECB";
$sEncryptionKey = "mysecretkey";
openssl_encrypt($sString, $sEncryptionMethod, $sEncryptionKey)
I would like to decrypt the same using Java/Scala?
String secret = "mysecretkey";
SecretKeySpec skeySpec = new SecretKeySpec(encKey.getBytes("UTF-8"), "AES");
byte[] decodedValue = Base64.getDecoder.decode(token);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
int decryptMode = Cipher.DECRYPT_MODE;
cipher.init(decryptMode, skeySpec);
new String(cipher.doFinal(decodedValue));
I am seeing the following error? how can we decrypt the same using Java?
Note: (decryption in PHP is working as expected) but I want to do this in Java
Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
The key has to be exactly 256 bit long. Clearly the PHP side is doing some unspecified magic voodoo to "mysecretkey" to obtain a 256 bit key. Java does not, as a rule, engage in 'the user does not appear to know what they are doing, eh, I'll take a wild stab in the dark', like PHP does, which is the problem here.
Figure out how "mysecretkey" is turned into a 256-bit key, and replicate that in java.
NB: ECB is extremely insecure. It sounds like you don't know enough about encryption to have any hope of producing an application that is actually hard to trivially break.
NB2: Note that the PHP documentation itself strongly suggests that 'key' should be some cryptographically derived secure source of 256 bits. The fact that openssl_encrypt actually gives you an answer when you provide a broken key is somewhat eyebrow raising. See the various comments at the PHP manual on openssl_encrypt which clearly highlight some weirdness is going on there but none are clear enough to actually explain what PHP is doing here.
Is it possible to use SHA3-512(a subset of keccak available in Java9) to generate keys in Java?
I have searched through a lot of noise and documentation to try to figure this out.
Currently it seems SHA3-512 is available as a hash for MessageDigest but not for generating keys. My code below tries to generate keys predictably(for wallet purposes like BIP32 but beyond currency to blockchain uses)
https://github.com/devssh/BlockchainFullNode/blob/d2978e598b4cdecdf4b3337713b2c3e839a6b181/src/main/java/app/model/Keyz.java#L111-L128
public static String GenerateSeed() throws Exception {
SecureRandom random = new SecureRandom();
byte[] seed = random.generateSeed(512);
return Base64.getEncoder().encodeToString(seed);
}
public static Keyz GenerateKey(String seedString) {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
KeyPairGenerator keyGen1 = KeyPairGenerator.getInstance("ECDSA");
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256k1");
SecureRandom random1 = SecureRandom.getInstance("SHA1PRNG");
random1.setSeed(Base64.getDecoder().decode(seedString));
keyGen1.initialize(ecSpec, random1);
KeyPair keyPair1 = keyGen1.generateKeyPair();
PublicKey pub1 = keyPair1.getPublic();
PrivateKey priv1 = keyPair1.getPrivate();
//Keyz is a simple model that stores the 3 fields below and overrides equals and hashcode on those fields
return new Keyz("random", pub1, priv1);
}
As you can see, it uses SHA1PRNG to predictably generate keypair deterministically(I am fine with the security concerns on this) so that the keys can be recreated deterministically.
Here is a JUnit test to make sure the keys are deterministic(works for SHA1PRNG, needs to work in SHA3PRNG). Ideally what is needed is a SHA3-512 TRNG in the GenerateSeed and a SHA3PRNG in the GenerateKey. Since the keygenerator needs a SecureRandom I would be surprised if java.Security.SecureRandom is still on something as insecure as SHA1PRNG.
https://github.com/devssh/BlockchainFullNode/blob/d2978e598b4cdecdf4b3337713b2c3e839a6b181/test/main/java/app/model/KeyzTest.java#L16-L22
#Test
public void shouldReturnDeterministicKeys() throws Exception {
String seedString = GenerateSeed();
Keyz random1 = GenerateKey(seedString);
Keyz random2 = GenerateKey(seedString);
//This assertion works as we override equals and hashcode
assertEquals(random1, random2);
}
Can someone please let me know if they figured a way to get this to work
It seems what you are looking for is not available out of the box:
Note that SHA1 and SHA1PRNG are not equivalent. While the former is a hash algorithm, the latter is a pseudo random generation algorithm (that uses SHA1 to update its internal state, of course.) One trivial result of this difference is, SHA1 outputs a fixed size of bits, where SHA1PRNG outputs as many bits as you like.
Because of this difference, SHA3-512 cannot be used as PRNG directly, although it is available in Java. What you need to do is, implement a PRNG algorithm using SHA3-512 (this part is really tricky, since generating a pseudo random stream is quite difficult.) and register it through your custom Security Provider (like Bouncy Castle does) with some name MySHA3PRNG. After that, you can get an instance of it with name MySHA3PRNG as you do for SHA1PRNG. The rest remains as-is.
A major problem with this tricky part might be as follows: Quoting from here,
The paper "Sponge-based pseudo-random number generators" talks about just that and it also describes a clean and efficient way to construct a re-seedable PRNG with a (Keccak) sponge function. What you'll get is a PRNG based on a cryptographic hash function… with the usual security implications.
For example: the paper explicitly states that you should reseed regularly with sufficient entropy to prevent an attacker from going backwards on the period of the PRNG (which is probably what you've been hearing about).
However, what you need is a PRNG algorithm that does not need to be re-seeded. I hope you have sufficient theoretical background to prove that your custom PRNG algorithm is secure.
Good luck!
I see many examples where the secret key is generated this way:
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(KEY_LEN);
SecretKey secretKey = generator.generateKey();
I'm in doubt if there's a difference (even conceptual) with the following:
byte[] material = new byte[KEY_LEN / Byte.SIZE];
SecureRandom.getInstanceStrong().nextBytes(material);
SecretKey secretKey = new SecretKeySpec(material, "AES");
Stated that both methods are 3 lines, is there some practical impact in preferring the first over the second?
Thanks
You could look at the actual source code for generateKey() to see the difference but ultimately they are both going to do the same steps to generate an AES key. I would argue the latter
byte[] material = new byte[KEY_LEN / Byte.SIZE];
SecureRandom.getInstanceStrong().nextBytes(material);
SecretKey secretKey = new SecretKeySpec(material, "AES");
is a little more brittle for the average coder, requiring them to understand the SecureRandom class. If you eliminate the second line altogether the code runs just fine with an all zero key, an obvious vulnerability that's also easy for an attacker to check. Also, using generateKey() can produce a properly formatted key if the algorithm has some particular requirements. For example, the now obsolete DES and Triple DES algorithms had a weird parity bit in each byte that some DES implementations expected to see.
There are many reasons why you would want to use the KeyGenerator method, which was designed for the purpose:
readability: generateKey tells you exactly what the algorithm is doing;
portability: e.g. when choosing a different algorithm);
correctness: SecretKeySpec may not validate the key entirely;
security: you would leave the key material exposed in the material variable, which may not be cleared or even garbage collected after the key is not required anymore;
hardware support: importing key material is often not supported for hardware devices; keys should be generated on the device itself (using a specialized KeyFactory implementation).
There is no reason at all to use the second method. If you want to use a very specific random number generator for generating the key then you can use one of the specialized init methods but beware that this may not be compatible with hardware devices.
The two code examples seem to be doing the same and as already answered usually they are.
However using security devices (e. g. HSM, smartcards or other crypto devices) by default they won't allow exposing the key bytes so you would be able to generate a key from arbitritrary byte array only in very limited cases..
I am new in security and I'm wondering if I can make my program better, like changing or adding something to make it better ( more secure )
(I have doubt from the program output)
Here is the output:
Encrypted Message: +g#þóv«5Ùû`ž
keybyte: [B#71e7a66b
Original string: Message
Original string (Hex): [B#2ac1fdc4
Here is the code:
public class AES {
public static void main(String ... args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
final String Algo="AES";
String key = "aaaaaaaaaaaaaaaa";
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
MessageDigest sha= MessageDigest.getInstance("SHA-1");
keyBytes=sha.digest(keyBytes);
keyBytes=Arrays.copyOf(keyBytes, 16);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, Algo);
Cipher cipher = Cipher.getInstance(Algo);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] ciphertext = cipher.doFinal("Message".getBytes());
System.out.println("Encrypted Message: " +new String(ciphertext));
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] original = cipher.doFinal(ciphertext);
String originalString = new String(original);
System.out.println("keybyte: "+keyBytes);
System.out.println("Original string: " + originalString + "\nOriginal string (Hex): " +original);
}
}
No, this code is pretty terrible:
Your key is fixed and a string that is then hashed. This looks to me like your key is actually supposed to be a password. A single hash is not enough to derive a key from a password. You need to use a strong hashing scheme like PBKDF2, bcrypt, scrypt and Argon2. Be sure to use a high cost factor/iteration count. It is common to choose the cost so that a single iteration takes at least 100ms. See more: How to securely hash passwords?
Always use a fully qualified Cipher string. Cipher.getInstance("AES"); may result in different ciphers depending on the default security provider. It most likely results in "AES/ECB/PKCS5Padding", but it doesn't have to be. If it changes, you'll lose compatibility between different JVMs. For reference: Java default Crypto/AES behavior
ECB mode is pretty bad. It's deterministic and therefore not semantically secure. You should at the very least use a randomized mode like CBC or CTR.
Without authentication you have the threat of not detecting (malicious) modifications of your ciphertexts. It is better to authenticate your ciphertexts so that attacks like a padding oracle attack are not possible. This can be done with authenticated modes like GCM or EAX, or with an encrypt-then-MAC scheme through HMAC with a strong hash function such as SHA-256.
Because of these issues you should use a library instead. Try JNCryptor or this library.
There are at least two problems with your implementation:
Unless you specify otherwise, you are using AES in ECB mode. ECB mode is not secure regardless of what cipher is under the hood. There are a number of secure modes, but usually people implement CBC mode, which is accomplished by changing your Algo to "AES/CBC/PKCS5Padding" (which really is PKCS7 padding, Java just does not know better). Then, you need to choose an IV via SecureRandom() to encrypt this way. This OWASP example seems to do it right (FYI -- 99% of the implementations you will find on the web have security problems somewhere).
Your key is not a key, instead it is a password that is being turned into a key. You shouldn't be hardcoding this, but I assume you are doing that for proof of concept only. In any case, the problem is using a hash function such as SHA-1 to turn a password into a key is not a good decision because passwords tend to have low entropy and can be brute forced. For this reason, you should be using a function that is dedicated to resisting brute force when turning a password into a key. Such functions include pbkdf2, bcrypt, scrypt, and argon2. For more information, Troy Hunt (a .Net guy) gives a good overview on the problems with using something like SHA-1 (or anything from the SHA2 family for that matter) in this context.
The other thing to keep in mind is that encryption does not generally provide message integrity. What this means is that just because you have encrypted the data does not mean somebody cannot modify it, and your software will still decrypt the modified data without being aware that it is modified. If you need to know that your data was not modified, then you will need to add on something like HMAC, or else you will need to switch to a mode of operation such as GCM.
Finally, AES is an excellent choice for security. But you need to use it in the right mode, you need to implement it right, and you need to understand whether you need more than just encryption.
I'm using BouncyCastle to encrypt/decrypt some files using AES and PKCS5 padding in CBC mode :
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
Now two questions:
How can I check that the provided key for decrypting data is correct or not ?
How Can I check encrypted input is untouched (e.g. not changed by user using an HEX editor)?
Thanks
You can use an AEAD mode, like CCM or GCM, in place of CBC. These modes authenticate an encrypted message, so if the wrong key is used, or the cipher text has been altered, you can detect it. You wouldn't be able to distinguish these cases though.
There is support in Java 7's cryptography API for GCM, but the SunJCE provider that ships with Oracle's Java implementation doesn't support it yet. You can get support through third-party providers like BouncyCastle.
You can achieve the same things if you use additional cryptographic services, like a digital signature or message authentication code.
Encryption is not just about the algorithm and the encryption key, it's also a lot about
the system organization.
In general, you can't determine that the key is correct. Any key can be used to decrypt the
data that's supposed to be decrypted, but it's up to some other mechanism to tell you if that
is the "correct" result.
In general, you can't determine if the data to be decrypted is untouched, except through some
external check. It's a property of most encryption systems that changing any of the encrypted
data would change the decrypted output drastically, probably into something you'd interpret
as garbage.
You should add a MAC which first verifies the integrity of the message, and only then you should decrypt it. A common choice of MAC is HMAC with whatever hash function you prefer, such as SHA-2.
Instead of doing this yourself, it's often a good idea to use an authenticated cipher. AES-GCM is a common choice. But you need to be really careful to never reuse an IV in that case.
The JCE ciphers are usually very basic. If you need a full featured protection including integrity and key testing, you need to combine them. And as usual it is better to not device that yourself. So better opt for a more high level format like PKCS7/12 or PGP.
Depending on the Padding used some ciphers will give you a PaddingException when you try to decrypt it with the wrong key. For stronger integrity check I would use a padding consiting of HMAC bytes.
A pretty complete method is included in the JCE, it is the AESWrap algorithm. It requires padded data but will ensure integrity. It is best combined with a length byte as described in RFC 3537. Note, that this is only intended for smaller amounts of secrets (like symmetric keys). The RFC3537 padding is restricted to 255 bytes.
To use this with a password derived key, you can use this:
char[] pass = ... // your password
byte[] codeBytes = ... // up to 255 bytes you want to protect
// generate wrapping key from password
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[16]; rand.nextBytes(salt);
SecretKey kek = f.generateSecret(new PBEKeySpec(pass, salt, 1000, 128));
kek = new SecretKeySpec(password.getEncoded(), "AES"); // convert into AES
// RFC3537 padding (lengthbyte)
byte[] wrappedCodeBytes = new byte[codeBytes + 1 % 8];
System.arraycopy(codeBytes,0,wrappedCodeBytes,1,wrappedCodeBytes.length);
paddedCodeBytes[0]=(byte)codeBytes.length;
byte[] pad = new byte[paddedCodeBytes.length - codeBytes.length -1]; rand.nextBytes(pad);
System.arraycopy(pad,0,paddedCodeBytes,codeBytes.length+1,pad.length);
// AESWrap is WRAP_MODE:needs a SecretKey
SecretKey paddedCodeKey = new SecretKeySpec(paddedCodeBytes, "RAW");
// now wrap the password with AESWrap kek is 128 bit
Cipher c = Cipher.getInstance("AESWrap"); // default IV
c.init(Cipher.WRAP_MODE, kek);
byte[] result = c.warp(paddedCodeKey);
The unwrapping is left for the reader as an exercise :) The example code uses 128bit keysize, since more entropy cant be expected from the PBKDF2 anyway.
Note that this will detect wrong passwords with high probability, and some critics will see this as a weakness of AESWrap.
Take a look at this tutorial on BC encryption, specifically the InitCiphers methods, and in detail at the second code block which specifies the actual type of cipher.
How can I check that the provided key for decrypting data is correct or not?
According to JCE Javadocs, specifically the constructor of Class SecretKeySpec:
This constructor does not check if the given bytes indeed specify a secret key of the specified algorithm. For example, if the algorithm is DES, this constructor does not check if key is 8 bytes long, and also does not check for weak or semi-weak keys. In order for those checks to be performed, an algorithm-specific key specification class (in this case: DESKeySpec) should be used.
Note that Interface KeySpec lists all implementing classes, basically a list of validation options.
How Can I check encrypted input is untouched (e.g. not changed by user using an HEX editor)?
Indeed. That's a good one. 'Input' is pretty generic. Do you mean the actual content to decrypt? Well, if it's munged I believe it will not decrypt properly. Does that make sense?
IFF you are talking about the case of a key with parity bits being altered, as described in item (6) at the Bouncy Castle FAQ, you will have to do an actual parity check on the key. Only the first 56 bytes of the key are used for the encryption ops, and the last 8 bytes are reserved for parity checking. So, essentially, the last part of the 'key' can be changed and the first part is still useful. To detect whether either the parity or the key have been altered, you would run a parity check. I found this little ditty on doing a parity check. And, for more info on how parity is set in these keys, see comments in the JDK7 Crypto Provider source for Class DESKeyGenerator by Jan Luehe (near bottom) which discuss parity setting.
I recently had some interaction with BC, and I hope this info helps.