urldecode, base64 decode & AES decrypt in apigeex

we have an incoming request json payload as token. We need to url decode the input token, base64 decode the url decoded input token, base64 decode the secret key & use the decoded secret key to AES decrypt the base64 decode the url decoded input token & read the values in input to route to different backend.

Sample input :

{

"token" : "23nf9werwer%dgerugerg834rjef%2sxxasddxxxxxxxxxxxxxxxxxxx"

}

steps:

URL Decode the token
Base 64 Decode the value from URL Decode
Base 64 Decode the secret key
AES Decrypt using the decoded secret key
Do we have out of policies to achieve this?

Was trying to write a javascript for it & see if it is the right approach or not.

**********************

var data = context.proxyRequest; // read the input token
var key = context.getVariable('value'); // read the secret from the KVM

var decodedata = decodeURI(data); // URL decode
print("decodedata: " + decodedata);
var b64decodedString = atob(decodedata); //base64 decode
print("b64decodedString: " + b64decodedString);
var code = CryptoJS.AES.decrypt(b64decodedString, key); //aes decrypt based on base64 decoded key. also included this library file https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js.
print("code: " + code);
var decryptedMessage = code.toString(CryptoJS.enc.Utf8);

console.log("descrypt111: "+decryptedMessage);

 

do we have any better way to achieve this?

 

0 7 1,329
7 REPLIES 7

You could do AES Crypto in JavaScript but I wouldn't do it that way. Not if you want it to perform well.

I see the token. To URL decode you can use JavaScript like this:

 

<Javascript name='JS-Decode'>
  <Source>
var payload = context.getVariable('request.content');
payload=JSON.parse(payload);
context.setVariable('token', decodeURIComponent(payload.token));
  </Source>
</Javascript>

 

How is the secretkey passed? Where is it? I don't see it. You mention a key, but you don't describe where it comes from. KVM? a Properties file? Something else?

Also there are some parameters related to AES encryption that you have not specified: The Initialization Vector, and the mode.

Once you sort all that out, you can use this Java callout to perform AES crypto. The Java callout can base64-decode the key and the ciphertext before decrypting. That might look like this:

 

<JavaCallout name="Java-AesDecrypt1">
  <Properties>
    <Property name='action'>decrypt</Property>
    <Property name='mode'>CBC</Property>
    <Property name='source'>ciphertext</Property>
    <Property name='decode-source'>base64</Property>
    <Property name='key'>{variable.holding.encoded.key}</Property>
    <Property name='iv'>{variable.holding.encoded.iv}</Property>
    <Property name='decode-key'>base64</Property>
    <Property name='decode-iv'>base64</Property>
  </Properties>
  <ClassName>com.google.apigee.callouts.AesCryptoCallout</ClassName>
  <ResourceURL>java://apigee-callout-aes-crypto-20210409.jar</ResourceURL>
</JavaCallout>

 

The result goes into a context variable called "crypto_output". The result might be a byte array, in which case.... it is up to you to do something more interesting with it. If it's a string you can ask the callout to decode the byte array into a string using UTF-8. Check the readme for that callout for more details.

 

Dino , Below is the exact javacode that we are trying to replicate in apigee using javacallout policy provided by you above.

********************* sample java code ******************

import org.apache.commons.codec.binary.Base64;

 

import javax.crypto.BadPaddingException;

import javax.crypto.Cipher;

import javax.crypto.IllegalBlockSizeException;

import javax.crypto.NoSuchPaddingException;

import javax.crypto.spec.SecretKeySpec;

import java.net.URLDecoder;

import java.nio.charset.StandardCharsets;

import java.security.InvalidKeyException;

import java.security.NoSuchAlgorithmException;

 

public class Main {

 

    public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {

        String decode = URLDecoder.decode("input string from token", StandardCharsets.UTF_8);

        byte[] base64decoded = Base64.decodeBase64(decode.getBytes());

        Cipher cipher = Cipher.getInstance("AES");

        cipher.init(Cipher.DECRYPT_MODE, getSecretKey()); //initialize cipher to decrypt mode.

        byte[] decrypted = cipher.doFinal(base64decoded);

        System.out.println(new String(decrypted));

    }

 

    private static SecretKeySpec getSecretKey() {

        return new SecretKeySpec(Base64.decodeBase64("shared secret from KVM"), "AES");

    }

}

 

Input string from token - sample payload:

{

"token" : "23nf9werwer%dgerugerg834rjef%2sxxasddxxxxxxxxxxxxxxxxxxx"

}

1. we need to run this javacallout & only when there is a token like above & not for other payloads. can we achieve this using any condition?

2. we need to do the below steps as per javacode

  1. URL Decode the token
  2. Base 64 Decode the value from URL Decode
  3. Base 64 Decode the secret key
  4. AES Decrypt using the decoded secret key

3. secret is stored in KVM & pulled using a variable called value as below

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<KeyValueMapOperations continueOnError="false" enabled="true" name="KVM-GetKey" mapIdentifier="storekey">
<DisplayName>KVM-GetKey</DisplayName>
<Properties/>
<ExclusiveCache>false</ExclusiveCache>
<ExpiryTimeInSecs>48600</ExpiryTimeInSecs>
<Get assignTo="private.secret">
<Key>
<Parameter>value</Parameter>
</Key>
</Get>
<Scope>environment</Scope>
</KeyValueMapOperations>

please let me know what are all the changes needed in javacallout based on above needs?

Also on how to read the output from javacallout (which is a json payload) & based on the values need to route to different backend which is being done thru another kvm (already working on regular payload cases ).

1. we need to run this javacallout & only when there is a token like above & not for other payloads. can we achieve this using any condition?

Yes, the way to do it is to set a variable based on the presence of that token field in the payload. You can do that with an ExtractVariables policy, it looks like this:

 

 

<ExtractVariables name='EV-Token'>
  <Source>request</Source>
  <VariablePrefix>extracted</VariablePrefix>
  <JSONPayload>
    <Variable name='token'>
       <JSONPath>$.token</JSONPath>
    </Variable>
  </JSONPayload>
</ExtractVariables>

 

 

After that policy runs, then a variable called extracted.token will contain the value of the token, if it exists. If the token field is not present in the JSON payload, or if the payload is not JSON, or if there is no payload, then the extracted.token variable will be unset, null. Therefore you can test it with a Condition. So the flow looks like this:

 

 

  <Step> 
     <!-- extract the token if it exists -->
     <Name>EV-Token</Name>
  </Step> 
  <Step>
    <!-- conditionally execute the Java callout if the token has been extracted -->
    <Name>Java-DecodeAndDecrypt</Name>
    <Condition>NOT (extracted.token = null)</Condition>
  </Step>

 

 

2. we need to do the below steps as per javacode ...

Yes. That should be no problem if you're handy with Java.

3. secret is stored in KVM & pulled using a variable called value as below

Sounds good.

please let me know what are all the changes needed in javacallout based on above needs?

Well I think you just need to implement the logic as you described it. URL Decode the token. Base64 decode the token and the secretkey. Then AES Decrypt. Just what you said. You have the general pattern in your code. What I would do:

  1. parameterize the Java callout, so that you can pass in the secretkey and the token via properties. If you're writing your own Java callout, You can follow the example in the AES Crypto callout that I referenced above for handling parameters.
  2. in the source tree, create a couple junit test methods, with known good tokens and secret keys.

Also on how to read the output from javacallout (which is a json payload) & based on the values need to route to different backend which is being done thru another kvm (already working on regular payload cases ).

The JSON payload I suppose has a bunch of other fields. For this you can use the same method I described earlier, to extract the token: an ExtractVariables policy. Be aware you can include multiple Variable elements inside the JSONPayload element in that policy. So if you have 4 or 5 well-known fields, you can extract them all with a single policy. That would give you 4 or 5 context variables that you could then directly reference in your flow, within Condition elements.

Hi Dino, we need to perform sequence of steps for input as  URLDecode, base64decode , base64code the key & AES decrypt using the base64 decoded output.

looks like current jar doesnt support decode multiple times & also our AES decryption seems to be default/custom without IV (initialization vector) with ECB. Could you please help build a jar for this requirement?

Hi 

yes I am clear that the existing AES callout doesn’t meet your desires. If you re-examine my first reply here, I suggested using that callout to perform one of the steps, and I suggested using other things to perform the other steps. 

it seems you would like to perform all of the steps in Java logic. That’s a defensible choice. but the resulting Java callout is not general or re-usable. It would be very specific to your purposes. 

I am  willing and able to write Java callouts that are general and reusable, but it’s not appropriate for me to accept requests to write arbitrary. Java code for specific purposes. 

 I think you can do this yourself. You’re very close. Just follow the example in the AES callout. Write some tests. You’ll have exactly what you need. 

Hi Dino , our java team is looking for below dependencies/jar files to perform the maven deployment. Do you know where we could get these or download them?

<dependency>
<groupId>com.apigee.edge</groupId>
<artifactId>message-flow</artifactId>
<version>${apigee.message.flow.version}</version>
</dependency>
<dependency>
<groupId>com.apigee.edge</groupId>
<artifactId>expressions</artifactId>
<version>${apigee.expressions.version}</version>
</dependency>

From the README

Preparation, first time only: `./buildsetup.sh`

 

It should download the JARs and install them in the local .m2 cache