/ Development  

Unlock the secret to fortifying your platform; Take charge with this DSO manipulation

Hi there AppWorks fans,

Welcome to a new installment of AppWorks tips.

If you never heard of the term Data Source Object (DSO) you’ll be amazed by this post! Just read and learn…No need for further introduction on this post.


Let get right into it…

Let’s say you have an external database somewhere available, and you want to consume from it. A typical requirement in each project…right? The configuration of such connection happens in the artifact ‘System Resource Manager’. Time to open it and have a view like this for the database configurations (aka DSOs!):

db_pass_001

Hit the plus-icon to add a new connection with this input:

db_pass_002

I use a test_db database on my Postgres instance for the AppWorks platform for convenience! This is the script I used to create it:

1
2
3
4
5
6
7
cd /usr/bin
./psql -U postgres -h 127.0.0.1
CREATE DATABASE test_db;
CREATE USER test01 WITH ENCRYPTED PASSWORD 'test';
GRANT ALL PRIVILEGES ON DATABASE test_db TO test01;
\du
\q

You can evaluate the connection and save it. Great, you can now use this DSO connection in any of your service containers; Off-topic for now but look at a splendid example of consuming “Northwind” database data here.

Soooo, all is fine; Only, this time your database administrator is available to help you to fill in the credentials! AHA…What if the person is on vacation or ran over by a truck (let’s hope not!). Your life still needs to continue (at least in DEV) and exactly at that moment (murphy’s law!), you need to remove your DSO temporarily and have it restored after the things you wanted to check out. Now what?
Well, that’s the moment you want to back up your DSO configuration! How? Well, for this we dive into the ‘LDAP Explorer’ artifact to have a look at a “secret” location:

db_pass_003

Yes, my friend…Hit that small button at the right! The outcome will look like this (and see what’s there!):

1
2
3
4
5
6
7
8
9
10
11
12
13
<component name="Connection pool">
<jdbcDriver>org.postgresql.Driver</jdbcDriver>
<dbVendor />
<driverName>JDBC</driverName>
<jdbcDriverXAClass>org.postgresql.xa.PGXADataSource</jdbcDriverXAClass>
<defaultDatabase>test_db</defaultDatabase>
<connectionString>
<![CDATA[jdbc:postgresql://node1.awtips.com:5432/test_db]]>
</connectionString>
<userName>test01</userName>
<password>e0pBVkEtQUVTL0dDTS9Ob1BhZGRpbmd9qgmIKuwUst4ERQA9iw3McUe3edsKn4OV3pA7iPOxXpDCpxZOq1zeVEY=</password>
<level />
</component>

NICEEE…So, next time you (or your DB admin) create(s) a new DSO, make sure to pull a copy of this XML element! Once you remove the DSO and create a new one, don’t hit the test button, but only save it. This will create a new XML part with an empty <password/> element which you can overwrite from your backup! 😎

Notes:

  • This only works when the DB name is the same; I guess the password gets an encryption (or a hash) with the DB name.
  • First, I thought it was the name of the organization, but that’s not the case…as far as I’ve tried it all out (And the username!?…I don’t think so!).

Now the hard part…Can we decrypt the password-string!? 🤔 Well, %$@!%$@! hits the fan at my location, but have a look on the learning skills below.

For this part I would like to do some Remote debugging to get a glimpse of the code behind it. Before you can do this, you first need to find an entry point. That’s a simple step…Just make sure you fill in wrong credentials and hit the test button. You’ll get an error with a stacktrace looking like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
at com.opentext.xqy.jdbc.datasource.DataSourceCreator
.createPooledDatasource(DataSourceCreator.java:315)
at com.opentext.xqy.jdbc.datasource.DataSourceCreator
.identifyDatabaseVendorAndRegisterInterceptors(DataSourceCreator.java:276)
at com.opentext.xqy.jdbc.datasource.DataSourceCreator
.getManagedDataSource(DataSourceCreator.java:150)
at com.cordys.dsoconfig.types.CordysDBSO
.testConnectivity(CordysDBSO.java:89)
at com.cordys.dsoconfig.DSOConfigService
.testConnectivity(DSOConfigService.java:134)
at com.cordys.dsoconfig.DSOConfigSOAPService
.testConnectivity(DSOConfigSOAPService.java:230)
at com.cordys.dsoconfig.DSOConfigSOAPService
.process(DSOConfigSOAPService.java:352)

This testConnectivity method is interesting here and on ‘Unix’, you can simply find the related JAR with a command like this:

1
sudo find /opt/opentext/AppWorksPlatformCE/defaultInst/ -type f -name '*.jar' -print0 | xargs -0 -I '{}' sh -c 'jar tf {} | grep DSOConfigSOAPService.class && echo {}'

This result will send you to this JAR:
/opt/opentext/AppWorksPlatformCE/defaultInst/components/dbconnection/dbconnection.jar

Use these steps to put TomEE in “remote debug” mode!

1
2
3
systemctl stop tomee
export JPDA_ADDRESS="*:8000"
sudo JPDA_ADDRESS=$JPDA_ADDRESS sh /opt/tomee/latest/bin/catalina.sh jpda run

Watch the logging for: Listening for transport dt_socket at address: 8000
After this message you can connect to it from your IDE (see the mentioned “remote debugging” post)

After blood, sweat, and tears and two toooooo long evenings of figuring out the trick…I give up, but not without a result. Have a look at the interesting JUnit tests below where the last one gives me a Tag mismatch error. I couldn’t figure out why the test fails as doing the encryption directly in the same test (testEncryptDecryptApi) it’s all fine!? Very strange; Could also be me, overlooking something!? Have a comment…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@Test
void testEncodeDecode() throws NoSuchAlgorithmException, IOException {
String encode = Base64.getMimeEncoder().encodeToString(
PASSWORD.getBytes(StandardCharsets.UTF_8));
String decode = new String(
Base64.getMimeDecoder().decode(encode),
StandardCharsets.UTF_8);
Assertions.assertThat(decode).isEqualTo(PASSWORD);
}

@Test
void testEncryptDecryptApi() throws NoSuchAlgorithmException, IOException {
String encryptAndEncode = UserProfile.encryptAndEncode(
USERNAME,
PASSWORD);
if (UserProfile.isEncrypted(encryptAndEncode)) {
String decodeAndDecrypt = UserProfile.decodeAndDecrypt(
USERNAME,
encryptAndEncode);
Assertions.assertThat(decodeAndDecrypt).isEqualTo(PASSWORD);
}
}

@Test
void testEncryptDecryptDirect() throws NoSuchAlgorithmException, IOException {
EncryptionAlgorithm encryptionAlgorithm = new AESGCMAlgorithm();
byte[] encrypt = encryptionAlgorithm.encrypt(
USERNAME.getBytes(StandardCharsets.UTF_8),
PASSWORD.getBytes(StandardCharsets.UTF_8));
byte[] decrypt = encryptionAlgorithm.decrypt(
USERNAME.getBytes(StandardCharsets.UTF_8),
encrypt);
Assertions.assertThat(new String(decrypt, StandardCharsets.UTF_8)).
isEqualTo(PASSWORD);
}

@Test
@Disabled("Tag mismatch")
void testDecryptOnly() throws NoSuchAlgorithmException, IOException {
if (UserProfile.isEncrypted(PASSWORD_ENCRYPTED)) {
String decodeAndDecrypt = UserProfile.decodeAndDecrypt(
USERNAME,
PASSWORD_ENCRYPTED);
Assertions.assertThat(decodeAndDecrypt).isEqualTo(PASSWORD);
}
}

Eventually, I also started to ask questions to my ChatGPT friend on my findings about the encryption!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
===Me===
Can you tell me more about the AES GCM algorithm?

===ChatGPT===
Certainly! AES-GCM (Advanced Encryption Standard with Galois/Counter Mode) is a
widely used encryption algorithm known for its strong security properties and
efficiency. Here's a detailed overview:

AES (Advanced Encryption Standard)
AES is a symmetric key encryption algorithm, meaning the same key is used for
both encryption and decryption. It operates on fixed block sizes (128 bits) and
supports key sizes of 128, 192, or 256 bits. AES is a block cipher, transforming
fixed-size blocks of plaintext into ciphertext using a series of transformations
governed by the encryption key.

GCM (Galois/Counter Mode)
GCM is a mode of operation for symmetric key cryptographic block ciphers. It
combines the Counter mode of encryption with Galois field multiplication for
authentication, providing both confidentiality and data integrity.

Key Features of AES-GCM:
1. Confidentiality and Integrity:
AES-GCM provides both encryption (confidentiality) and authentication
(integrity). It ensures that the data has not been tampered with and
that it comes from a legitimate source.
2. Efficiency:
AES-GCM is designed to be efficient and parallelizable, making it suitable for
high-throughput environments. It is optimized for performance on modern
hardware, including CPUs with AES instruction set extensions.
3. Nonce:
GCM requires a unique nonce (number used once) for each encryption operation
with the same key. The nonce does not need to be secret, but it must be unique
to ensure the security of the encryption.
4. Authentication Tag:
AES-GCM generates an authentication tag (also known as a Message Authentication
Code or MAC) as part of the encryption process. This tag is used to verify the
integrity and authenticity of the data during decryption.

How AES-GCM Works:
1. Encryption Process:
Input: Plaintext, key, nonce, and optional associated data (AAD).
Counter Mode Encryption: Encrypts the plaintext using the AES block cipher in
Counter mode, producing ciphertext.
Authentication: Computes an authentication tag over the ciphertext and the
associated data using Galois field multiplication.
2. Decryption Process:
Input: Ciphertext, key, nonce, authentication tag, and optional associated data.
Authentication: Verifies the authentication tag using the associated data and
ciphertext.
Counter Mode Decryption: Decrypts the ciphertext using the AES block cipher in
Counter mode, producing the plaintext.

Use Cases:
Network Security: SSL/TLS for secure web communications.
Storage Security: Encrypting sensitive data stored on disk.
Secure Messaging: Protecting messages in communication applications.

===Me===
What do I need to decrypt an AES GCM encrypted string?

===ChatGPT===
To successfully decrypt an AES-GCM encrypted string, you need the
following components:

- Ciphertext: The encrypted data you want to decrypt.
- Nonce (IV - Initialization Vector): A unique number used during encryption.
- Encryption Key: The secret key used during encryption.
- Authentication Tag: A tag used to verify the integrity and authenticity of the
ciphertext. In AES-GCM, this tag is typically appended to the ciphertext.
- Associated Data (AAD): Optional additional data that was authenticated but not
encrypted. If AAD was used during encryption, the same AAD must be provided
during decryption.

===Me===
Can you give me some code examples in Java?

===ChatGPT===
...

See the results (incl. the ChatGPT example) on GitHub


I give it a “DONE” for the things I wanted to know; Looks like my “Tag Missing” error is related to a missing component that I couldn’t figure out where to set it. We didn’t crack the code, but we learned something here which was already worth the time. This post also remembered me of a coding session when “Blockchain” became a hype. I was curious, dove into code examples, but got a bit bored once all the encryption techniques passed the code lines. Well, that’s exactly what happened here. Maybe it’s good to not crack it, so we can all use the platform safely without being hacked? Again, comment me…Have an enjoyable day and we’ll see each other next week. Cheers!

Don’t forget to subscribe to get updates on the activities happening on this site. Have you noticed the quiz where you find out if you are also “The AppWorks guy”?