OverviewSome institutions have a requirement to access the sensitive information in Kuali systems externally. I want to have a look at how to decrypt data in Kuali externally given the use case of Business Intelligence Reports.
This all came from a case when I tried to decrypt values in KFS using the default encryption key, but could not. I tried all manner of debugging to prove I was using the correct key and algorithm, but it did not matter. I continued to encounter the following error:
I knew something had to be different, so finally, I decided to attack this at the byte level and examine bytes directly. When I used the UTF-8 encoded string for the default encryption key, I was always using the following bytes:
-20 -128 -70 -29 14 -92 -80 -75 36while KFS actually was hoping for
-20 -128 -70 -29 14 -92 0 0 0The length is the same, but there is additional padding at the end. The padding is significant. Whenever I tried to apply the default encryption key, the padding did not match, so I was forced to reproduce with a byte array. No matter what, it looked as though I was going to have to make some modifications to this service to do what I want. I decided to go the extra mile and break it down into some changes and best practice improvements.
1 Setting Up a New Encryption KeyOne of the first improvements I'd like to make is not using a String-based key. Keys are natively non-String formats. In order for them to be embedded in text files and human-readable, we Base64 encode them. Though this is a workable solution, it is not preferred. Also, as we have seen, it does not always produce reliable results. Rather, let us utilize the rice.keystore and embed our key within it using the appropriate tools.
That is the existing rice private key. I want to add another.
I have now created a new DES key with the same password as the keystore.
To use this new service, I had to update several spring files in rice and KFS. For example, the spring-kfs-rice-overrides.xml:
Notice, I'm passing the location of the keystore and the passkey into the constructor.
The reason for that is best explained by showing the modified service I created:
I modified it to not be dependent upon external libraries (you will see why in a moment). I also modified it to read the key from the keystore instead of from a string. Another thing I did was added a main method. This way, the service can be run from the command line. It acts as a utility as shown below:
Now I have the option of using the string as a key or the java code representing the actual key. This java code will be useful later.
Once you start using your system with encrypted data (this can actually be on submitting your first document since document data is encrypted), changing your key becomes very difficult. You need to be able to decrypt and then re-encrypt. Unfortunately, such a method is not available in Kuali software right now. It is possible to do though.
First, you need to determine which fields are encrypted. Below is an incomplete list between KFS an Rice:
- KRNS_LOOKUP_RSLT_T.SERIALZD_RSLTS (Probably not important)
The above tables may have entire columns encrypted. One interesting caveat is that the KRIM_ENTITY_EXT_ID_T.EXT_ID column may not be entirely encrypted. Some values may be encrypted and some may not. The only way to know when a field is encrypted is when KRIM_EXT_ID_TYP_T.KRIM_EXT_ID_TYP_T is 'Y'.
The following SQL will reveal the fields that are encrypted.
2 Decrypting via OracleThere are several methods for decrypting via Oracle, but if I want to use the exact same method as I'm using in Kuali, it's just easier to update Oracle directly. Below is the following SQL for adding the modified decryption class to Oracle.
2.1 Modified Decryption Class
2.2 Calling Java from OracleNow I can call decrypt fields in oracle like this:
You may have noticed that the field is passed in directly without any key. The reason for this is that I have embedded the key into the class. Normally, this is a pretty bad programming practice, but it is the best way the key is used correctly from Oracle.
3 Changing Your Encryption KeyThat's just crazy talk! If you need to though, the EncryptionService has an encrypt method that will allow you to encrypt strings.
4 Alternative Encryption Algorithms
4.1 SSH RSA KeyMy favorite use case is to use your system's SSH key for encrypting keys. This way your key is specific to an environment and uses SSH more ubiquitously. This is probably not ideal for everyone, but I personally favor it.
4.1.1 Convert SSH Public Key to PEM Format
4.1.2 Import RSA Key into the keystoreJust like with the DES key, we will add our RSA key to the keystore. I created another custom encryption service class with a main method that handles this:
That's right. It uses the bouncycastle api. I include the jar in the classpath during execution. What happens is my RSA private key and its X.509 PEM counterpart are imported into the rice.keystore. To verify, try this:
You can see that now my RSA private key and SHA1 signed certificate are in the rice.keystore.
4.1.3 Using the RsaEncryptionServiceImplYou use it pretty much the same as my modified DemonstrationGradeEncryptionServiceImpl. Just add the spring bean and change the class to use the RsaEncryptionServiceImpl class.
4.2 PGPPGP (Pretty Good Privacy) is another private/public key encryption algorithm similar to RSA. It is a little bit of a diversion from RSA in java because we cannot use the keytool. Rather, we will use the PGP Keyring instead. For this specific reason, it follows after the RSA encryption method. The preference is to always use the java keystore whenever possible.
4.2.1 Setup PGP KeyringTo get started, we need a PGP Keyring.
Now I have two sets of keys:
In PGP, the Private Key is called a Secret and the Public Key is a ... Public Key.
Here is the encryption service
To use it, just make the following modifications to the spring-kfs-rice-overrides.xml