KMAC algorithm for KeyGenerator in Java - java

One can generate a symmetric key for use with HMAC-SHA256 with:
String algorithm = "HmacSHA256";
KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm);
keyGenerator.init(256);
SecretKey secretKey = keyGenerator.generateKey();
byte[] key = secretKey.getEncoded();
What algorithm name should be used for KMAC?

KMAC bases on KECCAK/SHA-3 which is available since Java 9.
Unfortunately this does only apply to the basic SHA-3 algorithm, not it's [H]MAC implementation (checked in Java 11).
In Java 17 there the following SHA-3 based Mac implementations are available:
HmacSHA3-224
HmacSHA3-256
HmacSHA3-384
HmacSHA3-512
If I understand it correctly none of them is equivalent to KMAC. KMAC is a special MAC implementation designed for KECCAK and it's properties.
So the only alternative is a third party implementation of KMAC like the one from BouncyCastle: https://javadoc.io/static/org.bouncycastle/bcprov-jdk14/1.69/org/bouncycastle/crypto/macs/KMAC.html

Related

How to convert crypto from java to nodejs

I have following code in Java.
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(password.getBytes());
kgen.init(INIT_LENGTH, secureRandom);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] result = cipher.doFinal(byteContent);
return result;
I need to implement it in JavaScript/Node.js
I don't know how to implement it in JavaScript from secretString to key generated by KeyGenerator
from the comment section:
This is my code:
function encodeDesCBC( textToEncode, keyString, ivString ) {
const key = Buffer.from(keyString.substring(0, 8), 'utf8')
const iv = Buffer.from(ivString.substring(0, 8), 'utf8')
const cipher = crypto.createCipheriv('aes-128-cbc', key, iv)
let c = cipher.update(textToEncode, 'utf8', 'base64')
c += cipher.final('base64')
return base64url.escape(c)
}
My problem is secureRandom and KeyGenerator. I do not know how to implement it in nodejs
I don't know Java, but your usage looks somewhat weak, there are algorithms like PBKDF2 (which is old and discouraged now) and scrypt which do a much better job at turning human passwords into keying material. I'm also not sure where your IV is coming from in your Java code. Exactly replicating the Java code would be somewhat difficult as you'd need to know how your version of Java was implemented, and hence how the bytes passed to setSeed actually get turned into a key.
Node's Crypto module, as far as I can tell, assumes you know how long the keys are supposed to be. In the case of AES 128 in CBC mode, this would be 128 bits (i.e. 16 bytes) for both the key and IV.
Assuming you wanted to use things built into the Crypto module (argon2 would be recommended if you could relax this restriction) then you'd do something like:
const crypto = require('crypto');
const password = 'passw0rd';
const scrypt_salt = crypto.randomBytes(16);
const key = crypto.scryptSync(password, scrypt_salt, 16);
which would leave you with a suitable value in key, then you'd encrypt with:
const plaintext = 'the plain text to encode';
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
const ciphertext = Buffer.concat([
cipher.update(plaintext),
cipher.final(),
]);
and you could encode to Base64 with ciphertext.toString('base64').
Note that to be able to decrypt this, you'd need the scrypt_salt, iv, and ciphertext.
When you encrypt something using AES, there is always a "mode" in play. In your Java code you don't specify a mode explicitly - Cipher.getInstance("AES"). When you do not specify a mode, the default mode in Java is "ECB" which is the most insecured mode anyway.
In your NodeJs code, you're using "CBC" mode which is a altogether different mode.
Neither "ECB", nor "CBC" are considered secured enough. As of today, usually, the recommended mode is the GCM mode.
To generate a key from a password, ideally a "key derivation function" should be used. The 4 key derivation functions recommended by OWASP are: PBKDF2, Scrypt, Bcrypt and Argon2.
In your Java code, the password is used as a seed for the pseudo random number generator class SecureRandom. That's a little bit bizarre because even if you give the same password to your function, it will produce different key in different run. Yes, SecureRandom is also used to generate key. But if the requirement is to generate a key from a password, a key derivation function, as mentioned above, shoul be used. Both the approaches are shown in the following StackOverflow answer with detailed explanation. However, it uses "GCM" mode. But as long as you understand the concepts, you can use any mode of your choice.
https://stackoverflow.com/a/53015144/1235935
Similarly, you'll find the same implementation in NodeJs in the following StackOverflow answer:
https://stackoverflow.com/a/53573115/1235935
To further understand AES in general, you may want to go through the following StackOverflow answer:
https://stackoverflow.com/a/43779197/1235935

Android AES/ECB/PKCS7PADDING generate key bouncy castle

I want to generate Random key using AES/ECB/PKCS7PADDING but java does not support PKCS7 padding and in my algo I have to use the same padding, bouncy castle does support PKCS7PADDING but I am unable to understand how to generate key using that
My code :
Security.addProvider(new BouncyCastleProvider());
private byte[] generateSessionKey() throws NoSuchAlgorithmException, NoSuchProviderException {
KeyGenerator kgen = KeyGenerator.getInstance("AES/ECB/PKCS7PADDING", "BC");
kgen.init(SYMMETRIC_KEY_SIZE);
SecretKey key = kgen.generateKey();
return key.getEncoded();
}
I am receiving error of no such algorithm
Key generation for AES does not have to do anything with padding. Padding is required for some modes of operation such as ECB and CBC. But the key itself doesn't rely on the mode or the padding. So you should just use:
KeyGenerator kgen = KeyGenerator.getInstance("AES", "BC");
which will undoubtedly work.
Do not use PKCS#7 padding, substitute "PKCS5Padding" for "PKCS7Padding".
Please take a look here to understand the difference between the padding mechanisms and here for the difference in Java.
I guess Android is using a stripped down Bouncy Castle provider (it was one of the reasons that SpongyCastle was lounged), which would explain the differences between Android and the Java SE provider.

SecretKeyFactory doesn't support algorithm "PBEWithHmacSHA256AndDESede"

I'm working on Java 1.7 - Window 7 (64 bit). I want to get SecretKeyFactory instance and Cipher instance of PBEWithHmacSHA256AndDESede algorithm. Unfortunately, I only got exceptions
java.security.NoSuchAlgorithmException: Cannot find any provider supporting PBEWithHmacSHA256AndDESede
at javax.crypto.Cipher.getInstance(Cipher.java:524)
Can you share me your solution of this problem?
The solution to this problem is two-part:
First, you need to call SecretKeyFactory.getInstance("PBEWithHmacSHA256AndDESede") instead of Cipher.getInstance("PBEWithHmacSHA256AndDESede"), as indicated by your stacktrace.
Second, you need to find the security provider that supports that algorithm. Neither SunJCE no BC 1.50 support it, by the way, so you will have to search for more exotic variants.
You can use the following code to check all installed providers and SecretKeyFactory algorithms supported by them:
// Security.addProvider( new BouncyCastleProvider() );
for ( Provider provider : Security.getProviders() ) {
System.out.println( provider );
for ( Provider.Service service : provider.getServices() ) {
if ( "SecretKeyFactory".equals( service.getType() ) ) {
System.out.println( service );
}
}
}
BC 1.50 definitely supports PBE with SHA-256 and AES variants under the names PBEWITHSHA256AND128BITAES-CBC-BC (OID 1.3.6.1.4.1.22554.1.2.1.2.1.2), PBEWITHSHA256AND192BITAES-CBC-BC (OID 1.3.6.1.4.1.22554.1.2.1.2.1.22) and PBEWITHSHA256AND256BITAES-CBC-BC (OID 1.3.6.1.4.1.22554.1.2.1.2.1.42).
It also supports a SecretKeyFactory algorithm with the name PBEWITHHMACSHA256 and OID 2.16.840.1.101.3.4.2.1, but that OID designates the plain SHA-256 hash function, meaning that this secret factory will use only hash instead of hash+cipher.
Have you:
Confirmed that security provider you are using supports 'PBEWithHmacSHA256AndDESede'
Have you installed the unlimited crypto -> http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
If the current security provider does not support 'PBEWithHmacSHA256AndDESede' use https://www.bouncycastle.org/ and be sure to add it as a security provider.
As #user3465651 points out, you have to have a library that supports this, such as BouncyCastle. In my case the error was:
java.security.NoSuchAlgorithmException: PBEWithSHA256And256BitAES-CBC-BC SecretKeyFactory not available
In troubleshooting, you can explicitly specify the library that contains the algorithm this way:
SecretKeyFactory.getInstance("PBEWithSHA256And256BitAES-CBC-BC","BC");
If you've done that you may still get this error if you're using Maven's shade plugin:
java.security.NoSuchProviderException: JCE cannot authenticate the provider BC
The shade plugin breaks the the cryptographic signature (explanation).
You can instead use the executable packer maven plugin solution that uses a jar-in-jar approach which preserves the signature for JCE in a single, executable jar.
Try this:
Old code:
String passphrase = "test";
KeySpec keySpec = new PBEKeySpec(passphrase.toCharArray());
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
ecipher = Cipher.getInstance(key.getAlgorithm());
...
ecipher.init(Cipher.ENCRYPT_MODE, ket, paramSpec);
...
New Code :
String passphrase = "test";
KeySpec keySpec = new PBEKeySpec(passphrase.toCharArray());
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
ecipher = Cipher.getInstance("PBEWithMD5AndDES");
...
ecipher.init(Cipher.ENCRYPT_MODE, ket, paramSpec);

AES-256 encryption with BouncyCastle Lightweight API

I have written some (functional) AES encryption code using Java's built in encryption libraries, as follows, but I'd like to use a 256-bit key. However, I'd like to do this without the user having to install to Unlimited Strength Cryptography Policy files.
Now, I've heard that using the BouncyCastle Lightweight API can allow me to do this, but unfortunately I'm having a great deal of trouble getting my head around it, and am struggling to fit any documentation that helps me.
Here is a my current code, in which 'content' is the byte array to be encrypted:
KeyGenerator kgen = KeyGenerator.getInstance("AES");
int keySize = 128;
kgen.init(keySize);
SecretKey key = kgen.generateKey();
byte[] aesKey = key.getEncoded();
SecretKeySpec aesKeySpec = new SecretKeySpec(aesKey, "AES");
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.ENCRYPT_MODE, aesKeySpec);
byte[] encryptedContent = aesCipher.doFinal(content);
How would I go about re-implementing this with the BouncyCastle Lightweight API? Can anyone help me out and/or point me in the direction of some simple example code?
I'm also interesting in any other solutions that allow 256-bit key AES encryption without the need for the user to install the unlimited strength policy files.
Many thanks!
This question and answer is a useful starting point.
256bit AES/CBC/PKCS5Padding with Bouncy Castle
The next best place to look is the test code for the LW APIs and then the JCE Provider code. The JCE Provider code is a wrapper around the LW libraries - so if you want to know how to do it, that's the best place to see it.
By the JCE Provider code, I mean the BC implementation.

Java cryptography generated key portability

In Java, I'm generating and serializing a symmetric key for encryption purposes:
KeyGenerator keyGen = KeyGenerator.getInstance(algorithm);
SecretKey symmetricKey = keyGen.generateKey();
Base64.encode(symmetricKey.getEncoded(), new FileOutputStream(filename));
where Base64 is from the Bouncycastle cryptography package and algorithm is AES.
The key, when used with Oracle (Sun) JVM 1.6.0_21, works perfectly is moved from, e.i, Windows to Linux (even between 32/64 bits OSs).
On OS X (Intel), with Apple's JVM, the key is loaded without exception but every string encrypted on Windows or Linux generates a BadPaddingException.
A string is encoded with the following code:
Cipher cipher = Cipher.getInstance(algorithm, "BC");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
encryptedString = new String(Base64.encode(cipher.doFinal(string.getBytes())));
where algorithm is AES.
Any clues?
Padding has nothing to do with the key.
What padding algorithm are you specifying when creating the Cipher?
If you are literally using just "AES" as the cipher algorithm, you should be explicit about the mode and padding. Otherwise, the crypto provider is free to choose some default of its own, and that's likely to vary from machine to machine.

Categories