I've got a Java application that does AES-256-OCB. For this, the BouncyCastle crypto library is used. As-is, it uses the standard JCA interface, but this requires a special policy file to be installed to permit key sizes greater than 128 bits.
This is unsuitable in our environment, and it seems to me that we may be able to dodge this by using BouncyCastle's own lightweight API. I'm a bit confused by this API, however, and I was curious how I actually go about instantiating a cipher as AES/OCB/NoPadding.
I'm normally pretty good about reading documentation, but BouncyCastle's rather extensive options have me a bit confused.
How can I instantiate a BlockCipher object for 256-bit OCB mode with no padding, using the BouncyCastle lightweight API, and use this to encrypt and decrypt data? I've already got the key, IV and data as byte[]s.
Here's what I came up with reading through BouncyCastle's test code. It appears to function, although I've not compared the results with any test vectors.
Call with encrypt=true for encryption, encrypt=false for decryption. Set tagLen to the desired length of the AEAD tag in bits (eg. tagLen=128). Optionally set ad to associated data for validation, or leave null to skip. Returns a properly-sized byte array of resulting ciphertext or plaintext.
protected static byte[] processCipher(boolean encrypt, int tagLen, byte[] keyBytes, byte[] iv, byte[] in, byte[] ad) throws IllegalStateException, InvalidCipherTextException {
KeyParameter key = new KeyParameter(keyBytes);
AEADParameters params = new AEADParameters(key, tagLen, iv);
AEADBlockCipher cipher = new OCBBlockCipher(new AESEngine(), new AESEngine());
cipher.init(encrypt, params);
byte[] out = new byte[cipher.getOutputSize(in.length)];
if(ad != null) cipher.processAADBytes(ad, 0, ad.length);
int offset = cipher.processBytes(in, 0, in.length, out, 0);
offset += cipher.doFinal(out, offset);
return out;
}
Related
I'm trying to create a AES key with this code
public static SecretKey generateSecretKey() {
KeyGenerator generator;
try {
generator = KeyGenerator.getInstance(StaticHandler.AES_KEY_MODE); // Is "AES"
generator.init(StaticHandler.AES_KEY_SIZE); // The AES key size in number of bits // Is "128"
return generator.generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
however using this code for encrypting/decrypting
public static String encrypt(String data, SecretKey secret, Charset charset) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
return new String(cipher.doFinal(data.getBytes()), charset);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String decrypt(String data, #NonNull SecretKey secret, Charset charset) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret);
return new String(cipher.doFinal(data.getBytes()), charset);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
gets the error
java.security.InvalidKeyException: Parameters missing
I'm guessing I need to add some salt, though I don't know how to do that with a generated key. I would like to stray away from generating a password but if it's a securely generated password I wouldn't mind.
Edit: Just an after thought, should I use GCM or CBC encryption if I'm sending packets through the network? Remember I'm using randomly generated keys and I am not going to keep them for sessions, randomly generated per client and server session.
No, you don't need salt and your key is actuallly fine. CBC mode requires an IV (Initialization Vector), see wikipedia, and IV should be different for each piece of data encrypted, but each decryption must use the same value as the corresponding encryption did. (added) For CBC, though not some other modes, it is also vital for security that IVs not be predictable by an adversary; the simplest and most common way to achieve both uniqueness and unpredictability is to use a secure Random Number (aka Bit) Generator such as Java's SecureRandom. If you want to know about other methods, that is not really a programming issue and is better suited on crypto.SX or security.SX, where there are already several Qs.
You can either generate the IV explicitly and specify it to both encrypt and decrypt, or allow the encrypt operation to generate the IV itself, fetch it from the encrypt Cipher, and specify it to the decrypt Cipher. In either case the encryptor must provide the value the decryptor will use; a common approach is to simply concatenate the IV with the ciphertext (making it very easy to keep them matched up properly) but again there are other approaches discussed on crypto and security. See https://docs.oracle.com/en/java/javase/11/security/java-cryptography-architecture-jca-reference-guide.html in the sections named "Initializing a Cipher Object" (the two paragraphs just after the boxed block of method declarations) and "Managing Algorithm Parameters".
Also don't store ciphertext in a String. Java String is designed to handle valid characters not arbitrary bytes. 'Decoding' ciphertext to a String and 'encoding' it back to binary will almost always lose or alter some of the data, especially if you allow the Charset to differ at the two ends, and with modern cryptography any change at all to the ciphertext will destroy all or much of your data. Since ciphertext is bytes, it is best to handle it as byte[]; if that is not possible because you want to put it in something that is characters like a URL, use one of the many schemes designed to encode arbitrary bytes to text so that they can be recovered correctly: base64 (3 or 4 major variants, plus many minor ones), base32, hexadecimal/base16, URL 'percent' encoding, MIME quoted-printable, yencode, Kermit, PPP, etc. j8+ java.util.Base64 provides the newer base64 variants (i.e. not uuencode).
Conversely, although 'plaintext' in modern crypto can really be any form of data, if yours truly is text and belongs in a String you should encode it using a suitable Charset before encrypting, and decode using the same Charset after decrypting, i.e.
byte[] ctext = encCipher.doFinal (input.getBytes(charset));
...
String output = new String (decCipher.doFinal (ctext), charset);
While the 'best' Charset may vary depending on your data, if you don't know what the data will be or don't want to bother analyzing it, UTF-8 is reasonably good for most text data and very popular and standard.
I am very new in the field of cryptography and have been stuck on this problem for two days.
I have a java code for AES/ECB encryption and I want my uwp app to use the same encryption technique but whatever I've tried so far gives different encryption results.
There are many answers on stackoverflow suggesting to use RijndaelManaged class, but this class is not available for UWP.
Here's java snippet
public string encrypt(String input, string key) {
SecretKeySpec skey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skey);
crypted = cipher.doFinal(input.getBytes("UTF-8"));
return Base64.encodeToString(crypted,Base64.NO_WRAP);
}
You need to use this answer to see how to encrypt/decrypt. However, you need a different (ECB so insecure) algorithm. So instead of the given CBC cipher mode you need to use AesEcbPkcs7 from the SymmetricAlgorithmNames.
Notes:
PKCS#7 is the same as PKCS#5 as used in the Java code, more info here;
you of course don't need to use an IV for ECB mode, so strip that out;
the UTF-8 encoding and base 64 decoding I'll leave out, it should be easy to do these encodings in any language/environment (the Convert and UTF8Encoding classes seem to be available for UWP apps).
I am using in my application AES algorithm to encrypt my data. My key is of 256 bit. The encrypted token formed is of this sort:
pRplOI4vTs41FICGeQ5mlWUoq5F3bcviHcTZ2hN
Now if I change one bit of the token alphabet from upper case to lower case say some thing like this:
prplOI4vTs41FICGeQ5mlWUoq5F3bcviHcTZ2hN
Some part of the token is getting decrypted along with junk value. My concern is why even some part of the data is getting visible when as such one bit is changed.My code for encryption is as follows:
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
Key secretKeySpecification = secretKeyData.getKey();
cipher.init(
Cipher.ENCRYPT_MODE,
secretKeySpecification,
new IvParameterSpec(secretKeyData.getIV().getBytes("UTF-8")));
byte[] bytesdata = cipher.doFinal(data.getBytes());
String encodedData = new BASE64Encoder().encode(bytesdata)
My code for decryption is:
Key secretKeySpecification = decryptionKeyDetails.getKey();
cipher.init(Cipher.DECRYPT_MODE, secretKeySpecification,
new IvParameterSpec(decryptionKeyDetails.getIV()
.getBytes("UTF-8")));
byte[] bytesdata;
byte[] tempStr = new BASE64Decoder()
.decodeBuffer(splitedData[0]);
bytesdata = cipher.doFinal(tempStr);
return new String(bytesdata);
Ciphertext modes of operation have specific forms of error propagation. There is such as thing as Bi-IGE (Bi-directional Infinite Garble Extension, that does change the whole plaintext if any error is introduced. However, it requires more than one pass, and it still won't protect you from getting random data if a bit was changed.
In the end, listen to Oleg and Codes (and Wikipedia and even me) and add an authentication tag to your ciphertext. Validate the authentication tag (e.g. HMAC) before decryption. Don't forget to include other data in your protocol such as the IV, or you may have a plaintext for which the first block has been changed.
I am creating a project to encrypt and decrypt a file. I have these two algorithms that work fine:
public static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
public static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
public static byte[] getRaw(String password_) throws Exception {
byte[] keyStart = password_.getBytes();
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr.setSeed(keyStart);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
byte[] key = skey.getEncoded();
return key;
}
Now I need to explain how it works. Does it use a private key? Where is the key storage? Can anyone help me?
Note: see owlstead's answer for an excellent description of the flaws in your code example
Your encrypt() and decrypt() operations are performing AES encryption and decryption respectively, using Java's JCE libraries. A JCE provider will be selected to perform the actual cryptography - the provider chosen will be the first in the list of providers that offers an implementation of AES. You have defined the algorithm as only "AES", so the mode of operation and padding will be chosen by the provider. If you want to control this, use the form "AES/mode/padding" (see the docs for valid choices)
The getRaw method derives an AES key from a password. The raw bytes of the password provide the seed for a random number generator. The random number generator is then used to generate sufficient key material for a 128-bit AES key. A different password will produce a different seed, which should produce a different stream of random bytes and thus a different key. I suspect this approach is weakened by the lack of entropy present in most people's passwords, leading to a reduced key space and easier attacks.
There is no key storage in your example code. JCE keys are normally persisted using a KeyStore object and the storage mechanism is provider-dependent.
The above piece of code is a bunch of crap. Unfortunately it is frequently used as a code snippet for Android related code (Android code uses the same API as Java, so there is no need for an Android specific example, andt unfortunately it specifically fails on Android).
I'll explain the issues:
Using a SecureRandom as Password Based Key Derivation Function (PBKDF) is completely idiotic. The underlying implementation of the SecureRandom implementation may change. Furthermore, it is not specified by the SecureRandom that calling setSeed() as the first method will replace the seed; it may actually add the seed to the current state - and this is what certain newer android versions do.
Cipher.getInstance("AES") actually uses the provider defaults instead of specifying the mode of operation and padding mode for the given cipher. By default the Sun provider will use ECB mode which is not suitable for encrypting most data.
String.getBytes() - which is used for the password - returns the platform default encoding. Different platforms may have different default encodings. This means that different platforms will generate different keys.
Above code does not add a message authentication code (MAC or HMAC). This may lead to an attacker changing random ciphertext blocks, which leads to random plain text blocks. This may lead to loss of confidentiality as well if padding Oracle attacks apply.
It seems to me that you are a beginner in cryptography. Please use a higher level standard such as RNCryptor compatible code, or use a standard such as Cryptographic Message Syntax (CMS).
I recently asked a question about Oracle Encryption. Along the way to finding a solution for myself I decided to move the encryption (well, obfuscation) to the application side for certain tasks.
My problem is that the database is already encrypting data a certain way and I need Java code to duplicate that functionality, so that text encrypted by one system can be decrypted by the other and vice versa.
I want the encryption to be compatible with what the DB was already doing but couldn't find the documentation that describes exactly what Oracle is doing. How do I replicate this in Java?
dbms_obfuscation_toolkit.DESEncrypt(
input_string => v_string,
key_string => key_string,
encrypted_string => encrypted_string );
RETURN UTL_RAW.CAST_TO_RAW(encrypted_string);
No matter what I try, it seems as if the Java DES encryption is different than Oracle's.
I found this works:
KeySpec ks = new DESKeySpec(new byte[] {'s','e','c','r','e','t','!','!'});
SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
SecretKey sk = skf.generateSecret(ks);
Cipher c = Cipher.getInstance("DES/CBC/NoPadding");
IvParameterSpec ips = new IvParameterSpec(new byte[] {0,0,0,0,0,0,0,0});
c.init(Cipher.ENCRYPT, sk, ips);
// or
c.init(Cipher.DECRYPT, sk, ips);
The missing piece was the Initialization Vector (ips) which must be 8 zeros. When you use null in Java you get something different.
Using Java in the database would have been another approach that would (should!) have guarenteed that the code (and hence results) would be identical.