Creating JWT in Java, hot to verifyi in Node? - java

How can a JWT created in Java with JJWT (https://github.com/jwtk/jjwt) be verified using Node.js? If we create the JWT like below and print the value out:
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary("something");
Key key = new SecretKeySpec(apiKeySecretBytes, SignatureAlgorithm.HS512.getJcaName());
JwtBuilder builder = Jwts.builder()
.setSubject(subject)
.signWith(SignatureAlgorithm.HS512, key);
System.put.println(builder.compact());
Trying to decode the printed out string in Node.js results to invalid signature error:
var nJwt = require('jsonwebtoken');
var token = 'eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJKb2UifQ.dqJqRrJ1zDlNq79RAJqhbqme23Fq_Fh9M4MufE5AT8A7-c2yo3OgG71RMSsUxloceG0WaYwhz3WIprcdZsYY_Q',
secretKey = 'something';
var verifiedJwt = nJwt.verify(token,secretKey);
console.log(verifiedJwt);
The token value is not correct in the example above, just provided as an example.

You're base64 decoding the String something when using JJWT, but you're not doing that when using nJwt.
The bytes of a String and the bytes resulting from Base64 decoding the same String are different byte arrays. So your two code samples are using effectively different keys.
edit:
Also, for what its worth, to ensure key integrity guarantees, you should ensure that your key when using HMAC-SHA algorithms is equal to or greater than the hash output length.
For SHA-256, the output length is 256 bits, SHA-384 is 384 and SHA-512 is 512. This implies a minimum key length of 32 bytes (32 ascii chars), 48 bytes, and 64 bytes, respectively. Using something as a key (9 bytes) for HMAC-SHA-512 is not advisable.
This is why JJWT provides convenience key generators to ensure best practices:
byte[] hmacSha512Key = MacProvider.generateKey().getEncoded();
byte[] hmacSha384Key = MacProvider.generateKey(SignatureAlgorithm.HS384).getEncoded();
byte[] hmacSha256Key = MacProvider.generateKey(SignatureAlgorithm.HS256).getEncoded();
HTH!

Related

How to use MySQL AES_DECRYPT to decrypt data encrypted with BouncyCastles PBEWITHSHA256AND256BITAES-CBC-BC Algorithm

We use BouncyCastle PBEWITHSHA256AND256BITAES-CBC-BC to encrypt data with our java application and store the encrypted result in a MySql Database.
Example Code:
StandardPBEStringEncryptor configurationEncryptor = new StandardPBEStringEncryptor();
configurationEncryptor.setAlgorithm("PBEWITHSHA256AND256BITAES-CBC-BC");
configurationEncryptor.setProviderName("BC");
configurationEncryptor.setSaltGenerator(new RandomSaltGenerator());
configurationEncryptor.setKeyObtentionIterations(1000);
configurationEncryptor.setPassword("aTestPassword");
String input = "A Test String!";
String cypherText = configurationEncryptor.encrypt(input);
String plainText = configurationEncryptor.decrypt(cypherText);
System.out.println("Input:" + input + " cypher:" + cypherText + " plain:" + plainText);
Output:
Input:A Test String! cypher:DhCSPbCWcZ76TUD/dDeGczlHbI9dQJyB2lKAiL7dDEk= plain:A Test String!
The cypher string above is a base64 encoded string which we store in our database.
I would now like to attempt to decrypt the cypher string stored in our database using the AES utilities provided by MySql.
I am trying to understand how the BC provider concatenates the encrypted data so that I can split it up and recreate the required parameters to enable me to decrypt the data with other tools - in this case MySql's AES_DECRYPT function.
Inspecting the code I can see that the first 16bytes of the cypher text (when base 64 decoded) is the salt, I am unsure where the init vector (IV) is stored in the remainder of the cypher text data.
If we can parse out the IV, salt and encrypted value from the string, then it should be possible to use external tools to decrypt the data.
A sample Mysql AES usage is as follows:
SET block_encryption_mode = 'aes-256-cbc';
SET #key_str = SHA2('aTestPassword',256);
SET #init_vector = RANDOM_BYTES(16);
SET #crypt_str = AES_ENCRYPT('A Test String!',#key_str,#init_vector);
SELECT AES_DECRYPT(#crypt_str,#key_str,#init_vector);
Output:
A Test String!
I would like to know how to parse the BouncyCastle cypher text to obtain its component parts, and also how to use the salt to generate the correct key hash with the number of iterations specified for use by Mysql to decrypt the data.
Any help much appreciated!
This answer is not a solution in code but will help you in finding the code.
First: you are NOT using Bouncy Castle to en-/decrypt directly - of course the cipher is used as provider for the en-/decryption.
The library that does the complete en-/decryption is JASYPT and here we can find answers for your question.
Base for my research is the GitHub https://github.com/jboss-fuse/jasypt/tree/master/jasypt/src/main/java/org/jasypt/encryption/pbe and I'm starting with "StandardPBEStringEncryptor.java":
As we are trying to understand the encryption in use I found
// The StandardPBEByteEncryptor that will be internally used.
private final StandardPBEByteEncryptor byteEncryptor;
and later the encrypt-method:
...
// The StandardPBEByteEncryptor does its job.
byte[] encryptedMessage = this.byteEncryptor.encrypt(messageBytes);
...
if (this.stringOutputTypeBase64) {
encryptedMessage = this.base64.encode(encryptedMessage);
result = new String(encryptedMessage,ENCRYPTED_MESSAGE_CHARSET);
} else {
result = CommonUtils.toHexadecimal(encryptedMessage);
}
As you get a Base64-encoded string this class just returns the encryptedMessage in Base64-encding.
Let's see the base class "StandardPBEByteEncryptor.java":
Searching for ivInUse:
// Initialization Vector to be used for encryption and decryption.
private byte[] ivInUse = null;
...
// Initialize Initialization Vector
this.ivInUse = new byte[algorithmBlockSize];
That means we do have a static IV of 16 bytes length (blocklength for AES) filled with "x00".
salt:
The DefaultSaltLength is set to 8 but when using a block cipher the salt length equals to the cipher block size (for AES 16):
// The salt size for the chosen algorithm is set to be equal
// to the algorithm's block size (if it is a block algorithm).
final int algorithmBlockSize = this.encryptCipher.getBlockSize();
if (algorithmBlockSize > 0) {
this.saltSizeBytes = algorithmBlockSize;
}
The salt is generated with the saltGenerator and after encryption it is concatenated with the ciphertext in the form salt|encryptedMessage:
encrypt:
...
// Finally we build an array containing both the unencrypted salt
// and the result of the encryption. This is done only
// if the salt generator we are using specifies to do so.
if (this.saltGenerator.includePlainSaltInEncryptionResults()) {
// Insert unhashed salt before the encryption result
return CommonUtils.appendArrays(salt, encryptedMessage);
}
The number of iterations is given by initialization (1000).
Last part to solve is the algorithm for the cipher-init and when using OpenJava 11 I find:
PBEWithHmacSHA256AndAES_256
that (hopefully) works in CBC-mode.

'javax.crypto.BadPaddingException' while using cipherInputStream

I'm writing a program to encrypt and decrypt data.
for encrypting,
I created a symmetric key using keyGenerator.
I transferred the key to the cipher, and created a string version of the key:
String keyString = Base64.getEncoder().encodeToString(symmetricKey.getEncoded());
in order to store it in a configuration file (so I can retrieve the key in the decrypt function).
Now, in the decrypt function I need to get that string back to key format, so I can send it as a parameter to the cipher in dercypt mode.
I convert it back to key this way:
byte[] keyBytes = key.getBytes(Charset.forName("UTF-8"));
Key newkey = new SecretKeySpec(keyBytes,0,keyBytes.length, "AES");
And I transffer it to the cipher and write the output (the decrypted data) using CipherInputStream:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, newkey, newiv, SecureRandom.getInstance("SHA1PRNG"));
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(encryptedBytes), cipher);
ArrayList<Byte> decryptedVal = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
decryptedVal.add((byte) nextByte);
}
byte[] bytes = new byte[decryptedVal.size()];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = decryptedVal.get(i);
}
String decryptedData = new String(bytes);
cipherInputStream.close();
System.out.println("decryptedData: " + decryptedData);
I get this error:
Exception in thread "main" java.io.IOException: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
So I suspect that there might be a problem with the way I treat the key.
Any suggestions? help would be appreciated!
I think you have not sent IV to decryption function. For decryption in CBC mode, you must provide an IV which is used in encryption process.
Update:
IV will affect only first block in CBC decryption mode. So my answer may affect the unpadding if your data is less than 1 block. It will just change the decrypted plaintext of the first block otherwise.
Of course you get this error: first you apply base 64 encoding:
String keyString = Base64.getEncoder().encodeToString(symmetricKey.getEncoded());
and then you use character-encoding to turn it back into bytes:
byte[] keyBytes = key.getBytes(Charset.forName("UTF-8"));
which just keeps be base64 encoding, probably expanding the key size from 16 bytes to 24 bytes which corresponds with a 192 bit key instead of a 128 bit key. Or 24 bytes key to a 32 bytes key of course - both seem to work.
To solve this you need to use Base64.getDecoder() and decode the key.
Currently you get a key with a different size and value. That means that each block of plaintext, including the last one containing the padding, will decrypt to random plaintext. As random plaintext is unlikely to contain valid padding, you will be greeted with a BadPaddingException.
Reminder:
encoding, e.g. base 64 or hex: encoding bytes to a text string
character-encoding, e.g. UTF-8 or ASCII: encoding a text string into bytes
They are not opposites, that would be decoding and character-decoding respectively.
Remarks:
yes, listen to Ashfin; you need to use a random IV during encryption and then use it during decryption, for instance by prefixing it to the ciphertext (unencrypted);
don't use ArrayList<Byte>; that stores a reference to each separate byte (!) - use ByteArrayOutputStream or any other OutputStream instead;
you can better use a byte buffer and use that to read / write to the streams (note that the read function may not fill the buffer, even if at the start or in the middle of the stream) - reading a single byte at the time is not performant;
lookup try-with-resources for Java;
using a KeyStore may be better than storing in a config file;
GCM mode (AES/GCM/NoPadding) also authenticates data and should be preferred over CBC mode.

Decrypting Payment Data with Visa Checkout

I am getting information back from Visa Checkout in an encrypted format. The guide on their site provides these instructions:
First, you must decrypt the dynamic key (encKey), then use the decrypted dynamic key value to decrypt the payment data payload (encPaymentData).
Follow these four steps to decrypt the encKey:
Base64-decode the encKey.
Remove the first 32 bytes of the decoded value. This is the HMAC (Hash Message Authentication Code). Calculate a SHA-256 HMAC of the
rest of the decoded data using your API Shared Secret and compare it
to the HMAC from the first 32 bytes.
The next 16 bytes should be removed and used as the IV (Initialization Vector) for the decryption algorithm.
Decrypt the remaining data using AES-256-CBC, the IV from step 3, and the SHA-256 hash of your API Shared Secret.
Follow these four steps to decrypt the encPaymentData using the
decrypted encKey:
Base64-decode the encPaymentData.
Remove the first 32 bytes of the decoded value. This is the HMAC. Calculate a SHA-256 HMAC of the rest of the decoded data using
the
decrypted encKey and compare it with the HMAC from the first 32
bytes.
The next 16 bytes should be removed and used as the IV for the decryption algorithm.
Decrypt the rest of the encPaymentData payload using AES-256-CBC, the IV from step 3, and the SHA256-hash of the
decrypted encKey.
I tried using ColdFusion but I am lost somewhat with the encryption issues, and am unable to fix the code. Below I have what is required. I am stuck on the step 3 & 4 where they say compare it and then decrypt it. Can someone guide what could be done to fix it?
enckey:
2M2WWOD4wANsGwWTmPqQIQYdz9WPwgiR0ntJHGaxm23b5a1sWUtCBcUQUMMtc9hvcYavJ6yqPgETOGZgDOdd9qjDwIb2aV9DLZT1iIcB3zNN5Ddhxd9iiui6TAlJxU/O
encPaymentData:
X2TXp0ZmwHrtfzSP5TPjUOjdZb0rjsHeDSqr8TwIF/VR8sMQhWN5hP4IRhQxWT CZcQhxZoUHP 0g/E/ot sjREAJ8YQf7c7jSzKsXRH/wrew5rQit2wJBlVSSZ YoLeIHbLrTz CfIoFv09hixl7ff27u0YCyV0zjP5vNfCBEIwfqyriqwXK2J QEOxQiKzDUW4br3o1t31aymCQC9eBBpoVKjFfSKlNXM9QEdNZBcLMZ8Wlv8lF/ua bnwshbM9u7Uhudqvut94RZEW NzkRD8MfBo12e/XhnL35qxGpHeQNPClC4EQDK6U/HmegeOj BZLbIIYBs6t9E8Q3AKBwfiPOFgB gSVnhXKnd3nKvllaG BaGrQJtk 7QAtnHMHxQAO5rdiS9465HCdiHa8zlv7SkvWh8EwcKCiT4qiZSM6QuYAeRSzDpPS1gsZ54Q9LizUnueH7yyzSd47cLPd0VlOQxobKtNN2LrsRb3IwOfzuwGnSRf2cNp49hBmmGP1b0BC hhB6UpCqP2ixTPvui NwMYzqZUe336bF1mfnKzEbEZIvIrPyx3uMiLDAns2g7S80gMNnHb/09i49xbfY3V7oudeiHV99FCh67DuG3uHE3/HzIZbcnxJwVJoJj6/3DuzK/Kw1JqSorE0M1qxUqoNkJC4aNCBrqfTlR7/eErrvB554TUZwcyQXqKCwrKv4NJEw6S0n3W1VASkfA0atbJQX2aLgx9kqnhYdDbaU8UcFIoeA45 yEuQ9vXzo2ILQhvamsAAFQd3i4mEOZ KNtMu25dDFlORn5C/oTZ1t1dzJoYMvq44gejp6L3IK e7JCugGchr963a2kd8NFa3wctRDHF8ChHxawVlU0aY7nasrVireMFLiM 9XIb4abfDtct/j1Q8IGN0hRkgCHO6dlnOrAaaQDYYH3axaMDp5Onb04whULYqGbn/XSR8Sn8gnoFbYqVJbR5aCp5Pe9TpfwxlEvV3z8ZfsASqW2y So9gpwg2y16K/FX3Io6kqeqUlAxbTRDfN/ofDIJaO H PUu2teqjvwvCkjPciGOQdXT5JxqoCiHqRwD0zeZPcG3b9Nfrq3Caf6zjwhf /CMeGc3dNHhSkXox R50MP8BlLWk/bXZRScTE/HSrVxE n073utHAnbVOM3gVha0Hr8TmoV8z1vBg5fY253so6kQX61ZIfHneCAZo0qeKRgDgLUryVPmUNc5 yKP8DxtmHl/0YUztpgyEx5njsrn1L 3EHMMUhui8d LQdNZoEpZ9U1Xb7XVsV5gnwR/mOITNOKJQsine4zMMHBcomHclrM0CuI58YrKPqworCmK6CYfzSc8UmXxXUe5dzND/DS9XgqDttQic2/OqTSAK63ynnrNqzr3D56VpDBeDeQjk3mc/0zmuFAPEXoAQoQKfD6HEuajvWJebQ6QIPgA TshqsnPlktbpftr4lsuB1tHS/W8D7SYVFMC/Kxy9QuYWs0cmRTtzfWEKIRHeDElOTQCX5JB5PgzVhhi5kYTi488Ba8j4zvNUw55hEoMxONYO7eMjJosmNjULsT492LGw3EfAgmgx9h3yFLQRZgfylg0h4PfLlcPOAdsnVX9/yLElD xu7Atwc4S7pBWTHvwue7PpRvWpTeqkU5sqiX4KcV5x8rk mBtxm48a8fsmp GNf 4IjwXu9cQaU9WLipiEnkqFsYo7/aAsmmKWBETyQg9BFXYK 165vrzSX8WTsv6ZZDnVjcE1n4Ov8Jl2cnAigoQbB0ROPpIRzZ3zH2diUv1vzlSuh9gbEJf3uQRKlYRVUbpboC0RbQ/7jgznfJAWyLykyDQ0EB8fVEOtbP1l4JEz39QwAU18ph3btnWWuKEV4 ghYvNG4m1DYntSF57s2ajRS6rPtR oYvGjrJL9zbHBhKHlfkIPC0TKotOCi96mqpikbBEfIZSomHxYgDwYCSvt60zaDIjlBxZ1UBdK JL0554Wia9W3Wg91bmYS9Q4SXMT8r4xGYB7OutEV24n7p088rVm/w2SZSiqlLqai539k6WGkzEQf19ytPtIE81a N z7aijTjy 7FCuVPF90svI5/NoGpSINqv84HUcMU71BvXUIT53Ea6CCpiWvvOPpo/XZar44emlIG0UgeB kfP6C6sis=
Secret code:
zRf7WZ3nM7ON{U0E6J5S}KpVm#k2ReDyq#1lG9go
CF Code:
<cfset str = "2M2WWOD4wANsGwWTmPqQIQYdz9WPwgiR0ntJHGaxm23b5a1sWUtCBcUQUMMtc9hvcYavJ6yqPgETOGZgDOdd9qjDwIb2aV9DLZT1iIcB3zNN5Ddhxd9iiui6TAlJxU/O">
<cfset tobas = tobase64(str)>
<cfset getFirst32bytes = Left(tobas,32)>
<cfset tobas2 = RemoveChars(tobas,1,32)>
<cfdump var="#tobas2#">
<cfset key = "zRf7WZ3nM7ON{U0E6J5S}KpVm#k2ReDyq##1lG9go">
<cfset x = hmac("#tobas2#","#key#","HMACSHA256")>
<cfset y = hmac("#getFirst32bytes#","#key#","HMACSHA256")>
<cfset decalgo = Left(x,16)>
<cfset decremainingData = RemoveChars(x,1,16)>
<cfset getDec = Decrypt(decalgo,"#key#","AES")>
<cfdump var="#x#"><br>
<cfdump var="#y#"><br>
<cfdump var="#decalgo#">
<cfdump var="#decremainingData#">
<cfdump var="#getDec#">
This is the java example they have on their site:
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String HASH_ALGORITHM = "SHA-256";
private static final String HMAC_ALGORITHM = "HmacSHA256";
private static final int IV_LENGTH = 16, HMAC_LENGTH = 32;
private static final Charset utf8 = Charset.forName("UTF-8");
private static final Provider bcProvider;
static {
bcProvider = new BouncyCastleProvider();
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(bcProvider);
}
}
private static byte[] decrypt(byte[] key, byte[] data) throws GeneralSecurityException {
byte[] decodedData = Base64.decode(data);
if (decodedData == null || decodedData.length <= IV_LENGTH) {
throw new RuntimeException("Bad input data.");
}
byte[] hmac = new byte[HMAC_LENGTH];
System.arraycopy(decodedData, 0, hmac, 0, HMAC_LENGTH);
if (!Arrays.equals(hmac,
hmac(key, decodedData, HMAC_LENGTH, decodedData.length– HMAC_LENGTH))) {
throw new RuntimeException("HMAC validation failed.");
}
byte[] iv = new byte[IV_LENGTH];
System.arraycopy(decodedData, HMAC_LENGTH, iv, 0, IV_LENGTH);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM, bcProvider);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(hash(key), "AES"),
new IvParameterSpec(iv));
return cipher.doFinal(decodedData, HMAC_LENGTH + IV_LENGTH,
decodedData.length– HMAC_LENGTH– IV_LENGTH);
}
private static byte[] hash(byte[] key) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance(HASH_ALGORITHM);
md.update(key);
return md.digest();
}
private static byte[] hmac(byte[] key, byte[] data, int offset, int length)
throws GeneralSecurityException {
Mac mac = Mac.getInstance(HMAC_ALGORITHM, bcProvider);
mac.init(new SecretKeySpec(key, HMAC_ALGORITHM));
mac.update(data, offset, length);
return mac.doFinal();
}
An important thing to understand about the sample code is that it refers to bytes. Your CF code is using characters. That might seem like a trivial distinction, but they are totally different things, which will produce very, very different results. In order to decrypt successfully, you need to work with the bytes (or binary) of the given strings - not characters.
Although it is possible to manipulate binary arrays using core CF functions, like arraySlice(), the syntax gets a little bulky/clunky at times. The reason is that binary arrays are a different type of object than your standard CF array, i.e. byte[] versus java.util.List. So depending on which functions are used, you may need javacast to coerce variables into the expected type. With that in mind ..
Part I - Decrypt the encKey
Base64-decode the encKey.
Remove the first 32 bytes of the decoded value. This is the HMAC (Hash Message Authentication Code). Calculate a SHA-256 HMAC of the
rest of the decoded data using your API Shared Secret and compare it
to the HMAC from the first 32 bytes.
First convert the base64 string into binary using binaryDecode. Then extract the appropriate number of bytes from the returned array. This is the expected HMAC value:
hmacSize = 32;
binaryToDecrypt = binaryDecode(encryptedKey, "base64");
expectedHMAC = binaryEncode( javacast("byte[]", arraySlice(binaryToDecrypt, 1, hmacSize))
, "hex" );
Next, extract all of the remaining bytes, and use them to calculate the actual HMAC. Verify it against the expected value. If the two do not match, something went wrong.
remainData = arraySlice(binaryToDecrypt, hmacSize + 1);
actualHMAC = hmac( javacast("byte[]", remainData ), sharedSecret, "HMACSHA256");
if (compare(actualHMAC, expectedHMAC) != 0) {
throw("ERROR: Invalid HMAC ["& actualHMAC &"]. Expected ["& expectedHMAC &"]");
}
The next 16 bytes should be removed and used as the IV (Initialization Vector) for the decryption algorithm.
The remaining bytes contains an IV, followed by the encrypted value. Before you can decrypt the latter, you need to extract and separate the two:
ivSize = 16;
ivValue = javacast("byte[]", arraySlice(remainData, 1, ivSize));
encryptedValue = javacast("byte[]", arraySlice(remainData, ivSize + 1));
Decrypt the remaining data using AES-256-CBC, the IV from step 3, and the SHA-256 hash of your API Shared Secret.
The last step before you can decrypt is to generate the decryption key, by hashing the shared secret. Unfortunately, CF's hash() function always returns a hex string. So it must be converted into base64 format to be compatible with the decryption function.
keyHex = hash(sharedSecret, "SHA-256", "utf-8");
keyBase64 = binaryEncode(binaryDecode(keyHex, "hex"), "base64");
Finally, use all three values to decrypt. The returned binary will contain the encryption key used in part II.
decryptedKeyBinary = decryptBinary( encryptedValue
, keyBase64
, "AES/CBC/PKCS5Padding"
, ivValue);
Part II - Decrypt the encPaymentData
Use the exact same process as in Part I, just swap the variables:
Use encPaymentData instead of encryptedKey
Use decryptedKeyBinary instead of sharedSecret.
The final, decrypted result will be binary. Use charsetEncode to convert it back into a human readable string:
result = charsetEncode(decryptedResult, "utf-8");
NB: The sample values you posted appear to be broken, as they do not even work with the java example. The steps above do produce the correct result when used valid values (key, data, etcetera).

Generating a Key from the first 10 Characters of a Base64 String

Let's say I have a Base64 String:
data = AOUTl5C2QV2xFRPlzKR0Ag==
I want to generate a Key in Java (Android) from the first 10 characters of this Base64 String and then use it to AES-Decrypt a Message sent from the Server. To do this, I use the below code:
String firstTen = data.substring(0, 10);
byte[] decodedBytes = Base64.decode(firstTen, Base64.DEFAULT);
SecretKeySpec key = new SecretKeySpec(decodedBytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] original = cipher.doFinal(Message_to_Decrypt, Base64.DEFAULT));
But then I can a Java.security.Exception:
java.security.InvalidKeyException: Key length not 128/192/256 bits.
Is there a way that I can get a valid Key which I can use for AES decryption from the first 10 Characters of a Base64String?
Extend the 10 characters with a hash function or better yet PBKDF2 (Password Based Key Derivation Function 2).
You really need to provide a key of an expected length, AES keys can be 128, 192 or 256 bytes long. While some AED implementations may null pad the key do not rely on that, it is not part of the standard.
The error message says: Key length not 128/192/256 bits.
You are using 10 Characters, each char is 8 bits. So 10*8=80. Try with 16 characters (128/8=16).

iOS symmetric key encryption / decryption equivalent to Java

I am trying to encrypt/decrypt data ios to java & java to ios but I data encrypted in java is not properly decrypted in ios & data encrypted in ios is not properly decrypted in java
- (NSData *) encrypt:(NSData *) dataToEncrypt symmetricKey:(NSData *)symmetricKey context:(CCOperation)encryptOrDecrypt{
NSUInteger data_length= [dataToEncrypt length];
uint8_t input_raw_data[data_length];
//The [dataToEncrypt length] gives the number of chars present in the string.So say there are 10 chars.
//Now,the getBytes needs to get the raw bytes from this i.e. binary NSData.But suppose the encoding was
//full 16 bit encoding then the number of bytes needed wd have been double- 20.But as we are using the
//NSUTF8StringEncoding,the number of byes needed is 1 per char as the chars(even if originally unicode are
//compressed into an 8 bit UTF8 encoding.)
[dataToEncrypt getBytes:&input_raw_data length:data_length];
// [dataToEncrypt getBytes:&input_raw_data maxLength:data_length usedLength:NULL encoding:NSUTF8StringEncoding options:0 range:NSMakeRange(0,data_length) remainingRange:NULL];
//According to the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t buffer_size = data_length + kCCBlockSizeAES128;
void* buffer = malloc(buffer_size);
size_t num_bytes_encrypted = 0;
CCCryptorStatus crypt_status = CCCrypt(encryptOrDecrypt, kCCAlgorithmAES128, 0x0000,
[symmetricKey bytes], kCCKeySizeAES128,
NULL,
input_raw_data, data_length,
buffer, buffer_size,
&num_bytes_encrypted);
// NSLog(#"~~num bytes encrypted: %d",num_bytes_encrypted);
if (crypt_status == kCCSuccess){
NSLog(#"~~Data encoded successfully...");
return [NSData dataWithBytesNoCopy:buffer length:num_bytes_encrypted];
}
free(buffer); //free the buffer;
return nil;
}
I have used this
Java Code -
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
String keyString = "keykeykeykeykeykeykeykey";
byte[] keyBytes = keyString.getBytes("UTF-8");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "AES"), new IvParameterSpec(new byte[16]));
byte[] resultBytes = cipher.doFinal("Hallo Welt!".getBytes("UTF8"));
FileOutputStream out = new FileOutputStream(new File("encryptedFileJava"));
out.write(resultBytes); out.close();
and this is encrypted text - “Se áJbë|8”R ,
key - BW3dKDf2bkDC4Bq9xTdr1g==
Please help me or suggest me any solution.
Thank you.
You have at least two problems:
The Objective C code is using ECB mode, while the Java code is using CBC mode. Use a bytearray of zeroes instead of NULL in the CCrypt-invocation to use CBC mode with a zero IV like the Java code.
Since keyBytes is 24 bytes long, Java will use AES-192. CCrypt will just ignore the extra bytes. Either specify AES-192 to CCrypt or use a 128 bit key ("keykeykeykeykeyk" should work).
For secured communication between IOS and Java devices, symmetric key encryption can be used.
In such cases where the platforms are different it is advisable that the key generated is a plain text key and not a API generated key.
AES 128 bit encryption can be used in such cases. IOS devices are capable of generating a symmetric key and encrypting the text using AES encryption.
Below link provides the java code to encrypt and decrypt using plain text symmetric key
http://www.java-redefined.com/2015/06/symmetric-key-encryption-ios-java.html

Categories