Encrypting and Decrypting data from App to server, ionic/angular -> java server - java

I want to encrypted data in ionic, send it to server by a POST request and decrypted on server side. And also received encrypted data from server and decrypted on the app.
The one I'm currently testing is AES256, but it seems that the encrypted data is different. Which means that it isn't encrypted or decrypted the correct way once that it gives 2 different values. DO you know some way to make it possible?
encrypt(secureKey, secureIV, data) {
this.platform.ready().then(() => {
cordova.plugins.AES256.encrypt(secureKey, secureIV, data,
(encrypedData) => {
console.log('Encrypted Data----', encrypedData);
this.decrypt(this.secureKey, this.secureIV, encrypedData);
}, (error) => {
console.log('Error----', error);
});
});
}
decrypt(secureKey, secureIV, encryptedData) {
this.platform.ready().then(() => {
cordova.plugins.AES256.decrypt(secureKey, secureIV, encryptedData,
(decryptedData) => {
console.log('Decrypted Data----', decryptedData);
}, (error) => {
console.log('Error----', error);
});
});
}
Using this methods on ionic to encrypt and decrypt strings of code. But The value of those on android device is different from the server running on java. How can I make it, to run the same processes for encrypting and decrypting data on the device as on the server?
UPDATE / SOLVED
So, I ignore the previous plugin of AES 256 of cordova, and install CryptoJS libraryCryptoJS library.
this was the result:
var key = await CryptoJS.enc.Latin1.parse("12345678910123456789012345678901");
var iv = await CryptoJS.enc.Latin1.parse("1234567891123456");
console.log(key + " ; " + iv)
var encrypted = await CryptoJS.AES.encrypt("test", key, { iv: iv },
{
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs5
});
console.log("encrypted -> " + encrypted.ciphertext.toString(CryptoJS.enc.Base64))
I had to use .Latin1.parse in order to generate the same value in TypeScript/JavaScript as in java. More info, here

Related

Reading Public Key Sent by Java Server in Swift

I'm trying to read a public key (x509 format encoded) from a Java Server to finalize my Elliptic Curve Diffie Hellman Exchange. I can send the Public Key to the server with no problem, but now I want to read the public Key that the server has sent to the iOS Client.
byte[] serverPubKeyEnc = serverKpair.getPublic().getEncoded(); (This is on the server)
This is what I return to the iOS side. For me to deal with it, I need to read it from the input stream and then turn it into a usable public key. This is what I have right now on the iOS side of things to read the key:
var error: Unmanaged<CFError>? = nil
let mutableData = CFDataCreateMutable(kCFAllocatorDefault, CFIndex(0))
if mutableData != nil
{
let headerSize = 26
//For importing Java key data
CFDataAppendBytes(mutableData, CFDataGetBytePtr(data as CFData), CFDataGetLength(data as CFData))
CFDataDeleteBytes(mutableData, CFRangeMake(CFIndex(0), headerSize))
//Use the mutableData here (SecKeyCreateWithData)
let publicKey = SecKeyCreateWithData(
mutableData!,
[
kSecAttrKeyType: kSecAttrKeyTypeEC,
kSecAttrKeyClass: kSecAttrKeyClassPublic,
] as NSDictionary,
&error)
let fullKey = SecKeyCopyExternalRepresentation(publicKey!, &error)
return fullKey!
}
Here I can read the "publicKey", I know it has some value inside it. How can I turn this into a usable key for me to generate the shared secret?
TLDR: I want to read a public key that comes from the Java server (ECDH) to generate a symmetric key for encryption in the iOS client.
The complete flow would look like:
receiving 91 bytes with the public key from server side
create a SecKey with SecKeyCreateWithData
create a key pair on iOS with SecKeyCreateRandomKey
send own public key to server side
server side can compute the shared secret with that information
client computes a shared secret with SecKeyCopyKeyExchangeResult
if everything is correct, it should give the same shared secret on iOS and Java side
Therefore, to get a complete test case, one can write a Java program that generates a key pair. For simplicity, one can copy/paste the public key between the Java and iOS app for a test instead of using a network connection. The Java program writes the public key to the console. This key is copied into the Swift source code. The Swift program is compiled and generates a key pair as well. The public key is copied / pasted to the Java program, which reads it on the console. Both programs then output the calculated shared secret, which should be the same for obvious reasons, since it is used for further symmetric encryption.
This fine answer https://stackoverflow.com/a/26502285/2331445 provides utility methods for converting a hex String to Data and back.
iOS Swift Code
The following code assumes that a secp256r1 curve is used with a key size of 256 bit.
The described flow could be implemented as follows:
let otherKey = "3059301306072a8648ce3d020106082a8648ce3d03010703420004df96b3c0c651707c93418781b91782319f6e798550d954c46ac7318c7eac130f96380991a93049059e03e4190dd147b64d6ebc57320938f026844bda3de22352".hexadecimal!
guard let otherPublicKey = otherPublicKey(data: otherKey) else { return }
guard let ownPrivateKey = createOwnKey() else { return }
guard let ownPublicKey = SecKeyCopyPublicKey(ownPrivateKey) else { return }
send(ownPublicKey: ownPublicKey)
if let sharedSecret = computeSharedSecret(ownPrivateKey: ownPrivateKey, otherPublicKey: otherPublicKey) {
print("shared secret: \(sharedSecret.hexadecimal)")
} else {
print("shared secret computation failed")
}
The used functions:
private func otherPublicKey(data: Data) -> SecKey? {
var error: Unmanaged<CFError>? = nil
let cfData = data.dropFirst(26) as CFData
let attributes = [
kSecAttrKeyType: kSecAttrKeyTypeEC,
kSecAttrKeyClass: kSecAttrKeyClassPublic,
] as CFDictionary
if let publicKey = SecKeyCreateWithData(cfData, attributes, &error) {
return publicKey
}
print("other EC public: \(String(describing: error))")
return nil
}
private func createOwnKey() -> SecKey? {
var error: Unmanaged<CFError>? = nil
let keyPairAttr: [String : Any] = [kSecAttrKeySizeInBits as String: 256,
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecPrivateKeyAttrs as String: [kSecAttrIsPermanent as String: false]
]
guard let key = SecKeyCreateRandomKey(keyPairAttr as CFDictionary, &error) else {
print("key creation: \(String(describing: error))")
return nil
}
return key
}
This function send only outputs the key in hex on the debug console. For the test it can be transferred to the Java program via copy/paste. In a real program it would be transferred to the server via a network connection.
private func send(ownPublicKey: SecKey) {
guard let data = SecKeyCopyExternalRepresentation(ownPublicKey, nil) as Data? else {
print("SecKeyCopyExternalRepresentation failed")
return
}
let secp256r1Header = "3059301306072a8648ce3d020106082a8648ce3d030107034200"
let pkWithHeader = secp256r1Header + data.hexadecimal
print("ownPublicKeyHexWithHeader \(pkWithHeader.count / 2) bytes: " + pkWithHeader)
}
With the own private key and the public key of the server, the shared secret can be computed.
private func computeSharedSecret(ownPrivateKey: SecKey, otherPublicKey: SecKey) -> Data? {
let algorithm:SecKeyAlgorithm = SecKeyAlgorithm.ecdhKeyExchangeStandard
let params = [SecKeyKeyExchangeParameter.requestedSize.rawValue: 32, SecKeyKeyExchangeParameter.sharedInfo.rawValue: Data()] as [String: Any]
var error: Unmanaged<CFError>? = nil
if let sharedSecret: Data = SecKeyCopyKeyExchangeResult(ownPrivateKey, algorithm, otherPublicKey, params as CFDictionary, &error) as Data? {
return sharedSecret
} else {
print("key exchange: \(String(describing: error))")
}
return nil
}
Test
In the upper area you can see the Xcode console and in the lower area the output of the Java program. The common secret is the same. So the test was successful.

Android convert AES key generated to string (base 64 / hex ...)

Problem Description:
I am working on an android app that will generate an AES key and store it in keystore.
Whenever I need to send data to a server, I use the AES key to encrypt data, use server public key to encrypt the AES key and send encrypted data + encrypted key to the server to decrypt the full load.
the code to generate the Key in android is below:
public void generateKey()
{
try {
if (!keyStore.containsAlias(KEY_ALIAS)) {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, AndroidKeyStore);
keyGenerator.init(
new KeyGenParameterSpec.Builder(KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(false)
.build());
keyGenerator.generateKey();
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
When I need to encrypt data i fetch the key using this function
private java.security.Key getSecretKey(Context context) throws Exception {
return keyStore.getKey(KEY_ALIAS, null);
}
Using this key I was able to encrypt the data. but the issue is trying to encrypt the key to send it to the server.
I tried to get the key as byte [] to encrypt it but using the function
key.getEncoded();
the resulting byte array is always null.
What is wrong here and how to solve it?
App is for Android 23+
I'm not sure, but you didn't save the key before you do the encryption, make sure you have it on android keystore before.
This kotlin example would help:
#Throws(IOException::class, KeyStoreException::class, CertificateException::class, NoSuchAlgorithmException::class)
fun saveKeyPair(pair: KeyPair, storeAlias: String) {
val certificate = generateCertificate(pair)
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore!!.load(null, null)
val x509CertificateObject = X509CertificateObject(certificate)
keyStore.setKeyEntry(storeAlias, pair!!.private, null, arrayOf<java.security.cert.Certificate?>(x509CertificateObject))
}
Hope that could help.

Getting error(Error: Unsupported state or unable to authenticate data) in nodejs decryption

I have encrypted a message using AES/GCM/NoPadding algorithm(AES-256) in java & trying to decrypt it in NodeJs. Getting exception "Error: Unsupported state or unable to authenticate data" while decryption. Below is the complete code of java and nodejs & error message:
Pl help me where is the incorrect code in java or nodejs.
Below is the code started with Java encryption code :
public static String encryptAES(String privateString, String skey) throws Exception{
byte[] iv = new byte[GCM_IV_BYTES_LENGTH]; //12 iv length
byte[] tag = new byte[GCM_TAG_BYTES_LENGTH]; //16 tag length
(new SecureRandom()).nextBytes(iv);
(new SecureRandom()).nextBytes(tag);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); //algorithm type
GCMParameterSpec ivSpec = new GCMParameterSpec(GCM_TAG_BYTES_LENGTH * Byte.SIZE, iv);
cipher.init(Cipher.ENCRYPT_MODE, getKey(skey), ivSpec);
byte[] ciphertext = cipher.doFinal(privateString.getBytes("UTF8"));
byte[] ivTag = new byte[GCM_IV_BYTES_LENGTH + GCM_TAG_BYTES_LENGTH]; // merging iv and tag
System.arraycopy(iv, 0, ivTag, 0, iv.length);
System.arraycopy(tag, 0, ivTag, iv.length, tag.length);
byte[] encrypted = new byte[ivTag.length + ciphertext.length]; //merging ivtag and cipher
System.arraycopy(ivTag, 0, encrypted, 0, ivTag.length);
System.arraycopy(ciphertext, 0, encrypted, ivTag.length, ciphertext.length);
String encoded = Base64.getEncoder().encodeToString(encrypted); //b64 encoded value
System.out.println("encrypted str:>" + encoded.length() + " | " + encoded);
return encoded;
}
//NodeJS decryption code :
function decryptTokenResponse(encryptedStr){
let data = encryptedStr
const bData = Buffer.from(data, 'base64');
const iv = bData.slice(0, 12);
const tag = bData.slice(12, 28);
const text = bData.slice(28);
var decipher = crypto.createDecipheriv(algorithm,masterkey, iv)
decipher.setAuthTag(tag)
var plainText = decipher.update(text,'base64','utf-8');
plainText += decipher.final('utf-8'); **//getting exception here**
console.log('Decrypted data = ' + plainText)
}
**//Error :**
internal/crypto/cipher.js:145
const ret = this._handle.final();
^
Error: Unsupported state or unable to authenticate data
at Decipheriv.final (internal/crypto/cipher.js:145:28)
at decryptTokenResponse (/home/jdoodle.js:40:27)
at Object.<anonymous> (/home/jdoodle.js:18:1)
at Module._compile (internal/modules/cjs/loader.js:678:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:689:10)
at Module.load (internal/modules/cjs/loader.js:589:32)
at tryModuleLoad (internal/modules/cjs/loader.js:528:12)
at Function.Module._load (internal/modules/cjs/loader.js:520:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:719:10)
at startup (internal/bootstrap/node.js:228:19)
Command exited with non-zero status 1
The authtag for GCM or CCM is generated by the encrypt operation -- you do not randomly generate it yourself (as you do, or at least can, for the IV/nonce). However it is sort of hidden, because Java crypto fits authenticated encryption into its preexisting API by appending the tag to the ciphertext returned by an encrypt operation, or input to a decrypt operation. OTOH nodejs/OpenSSL treats them as separate values. (Both Java and nodejs/OpenSSL treat AAD as separate, but you aren't using AAD.)
Since you are already packing things together (and base64ing) for transmission, you should:
in Java, concatenate the IV plus the return from cipher.doFinal (which is ctx + tag) forming IV + ctx + tag
base64 and send and after receiving de-base64 as you already do
in nodejs, split these into IV,ctx,tag which is easy because Buffer can slice from both ends: bData.slice(0,12) bData.slice(12,-16) bData.slice(-16)
Also your text is already de-base64-ed, but since it's a Buffer the inputEncoding to decipher.update is ignored.
You must provide the AuthenticationTag to createDeCipherIv() when using AES in GCM, CCM and OCBmodes.
Why would you implement GCM without it? You may as well use CTR mode of AES if you don't want the additional protections.
I some how figured it out that how it would work for me, I was getting the same error then observing it again and again for same encryption I found that whenever the encrypted output contains "/" or "+" character I get this same error. Then I used while loop to keep on encrypting again and again untill output doesn't contain there characters, and give the output which does not has any of these two characters. It worked for me this way.
Hope that It would work for you as well.

How to decrypt Triple Des CryptoJS values in Java class

I was asked to encrypt some text from client side ( web ) before sending it to server side ( java )
So i try to use CryptoJS library for client side.
I encrypt it like this :
var key = "aaaaaaaaaaaaaaaaaaaaaaaa";
var value = "KF169841";
var encryptedString = CryptoJS.TripleDES.encrypt(value, key);
console.log(encryptedString.toString());
And i get something like this : U2FsdGVkX19eYFFHgYGCr3v9/skTOKVp0pLWRNK9JTg=
I use this encryptedString and key in other Decrypt tool online ( Which also use CryptoJS ) and got back exact value KF169841.
After sending this value and key to server ( well key isn't sending directly to server though but for test, it is ), i need to decrypt it using Java.
But i quite don't know how to decrypt it. I'm tried some code from google search but it end up wrong padding if use DESese or get wrong value if i use ECB/NoPadding.
I did try to something like setting sfg for CryptoJS side like:
mode: CryptoJS.mode.EBC,
padding: CryptoJS.pad.NoPadding
But they got javascript exception ( a is not define )
So any have any experience with CryptoJS can help me decrypt this one using java ?
=============================================================
UPDATE : Sorry here my server side code i'm using
/**
* Method To Decrypt An Ecrypted String
*/
public String decrypt(String encryptedString, String myEncryptionKey) {
String decryptedText = null;
try {
byte[] keyAsBytes = myEncryptionKey.getBytes("UTF8");
KeySpec myKeySpec = new DESedeKeySpec(keyAsBytes);
SecretKeyFactory mySecretKeyFactory =
SecretKeyFactory.getInstance("DESede");
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
SecretKey key = mySecretKeyFactory.generateSecret(myKeySpec);
cipher.init(Cipher.DECRYPT_MODE, key);
// BASE64Decoder base64decoder = new BASE64Decoder();
// byte[] encryptedText = base64decoder.decodeBuffer(encryptedString);
byte[] encryptedText = org.apache.commons.codec.binary.Base64.decodeBase64(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= bytes2String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
According to the documentation, your encryptedString variable contains structured data that must be split apart to be sent to Java code. You will need to send encryptedString.iv and encryptedString.ciphertext to your Java code. If you continue to use passwords (see below), you will need to send encryptedString.salt as well.
If you pass your key as a string it will be interpreted as a password and a key will be derived from it. If you actually want to pass an explicit key, follow the documentation and specify the IV and key as suggested by the code snippet below. If you stick with supplying a password, then you must figure out the derivation scheme and use the same process in your Java code.
// Code snippet from http://code.google.com/p/crypto-js/#Custom_Key_and_IV
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script>
var key = CryptoJS.enc.Hex.parse('000102030405060708090a0b0c0d0e0f');
var iv = CryptoJS.enc.Hex.parse('101112131415161718191a1b1c1d1e1f');
var encrypted = CryptoJS.AES.encrypt("Message", key, { iv: iv });
</script>
Regarding your Java code, it looks mostly OK (although there is plenty of room for error with string conversions). However, you probably want to convert your key from hex to binary rather than grabbing the bytes:
byte[] keyAsBytes = DatatypeConverter.parseHexBinary(myEncryptionKey);
This assumes you alter your JavaScript code to pass the literal key value.
You will also need to switch to DESede/CBC/PKCS5Padding and pass an IVParameterSpec object to your Cipher.init call, specifying the IV value sent from your Java Script code.

Encrypting in Java and Decrypting in Javascript

I want to encrypt a string in Java and decrypt it Javascript. I tried Crypto in javascript, but decrypted incorrectly. What is the simplest way to decrypt in Javascript?
I used the encryption code from the following link:
http://bryox.blogspot.in/2011/12/encrypt-and-decrypt-string-by-java.html
What is the simplest way to decrypt in Javascript?
Insecurely.
Alternatively, if you're looking for the simplest way to securely encrypt/decrypt in Java and Javascript, you might want to take a look at libsodium, which has bindings for Java and JavaScript.
Encrypting in Java with LazySodium
LazySodiumJava lazySodium = new LazySodiumJava(new SodiumJava());
SecretBox.Lazy secretBoxLazy = (SecretBox.Lazy) lazySodium;
Key key = lazySodium.cryptoSecretBoxKeygen();
String msg = "This message needs top security";
byte[] nonce = lazySodium.nonce(SecretBox.NONCEBYTES);
lazySodium.cryptoSecretBoxEasy(msg, nonce, key);
Decrypting in JavaScript with Sodium-Plus
const {SodiumPlus, CryptographyKey} = require('sodium-plus');
let sodium;
async function decryptMessage(ciphertextHex, nonceHex, keyHex) {
if (!sodium) sodium = await SodiumPlus.auto();
let ciphertext = Buffer.from(ciphertextHex, 'hex');
let nonce = Buffer.from(nonceHex, 'hex');
let key = CryptographyKey.from(keyHex, 'hex');
return sodium.crypto_secretbox_open(ciphertext, nonce, key);
}
decryptMessage(ciphertext, nonce, key).then((plaintext) => {
console.log(plaintext.toString());
});

Categories

Resources