The ICM sometimes needs to encrypt (and decrypt) data, for example, for persistence or transport. Therefore, the ICM uses a separate library named com.intershop:encryption
. This library provides code that implements all the use cases required by the ICM. The library also provides a command-line tool that can be used, for example, to create an encryption configuration from scratch.
Phrase / Abbreviation | Meaning |
---|---|
JCE | |
Docker |
The encryption library consists of the following software components to implement the use cases:
The components have the following semantics / purpose:
Component | Description |
---|---|
EncryptionManager | This is the main component that actually implements the encrypt, decrypt and re-encrypt use cases. To do this, it uses several other components, most notably the |
KeyManager | Implements the lookup (and storage) of JCE-Keys. |
CipherFactory | Implements the lookup of JCE-Cipher instances. |
EncryptionConfigs | Contains 1 to n EncryptionConfig instances and implements the lookup. |
EncryptionConfig | Encapsulates a single encryption config, see Concept - Encryption. |
EncryptionConstraints | Implements some constrains to be fulfilled, especially regarding the |
EncryptedData | Encapsulates encrypted data (how it is stored or transported) and contains code to parse and render string representations of encrypted data, see Concept - Encryption. |
KeyManagerFactory | Implements the instantiation of a |
Keystore | Implementation of |
NullKeystore | NoOp-implementation of |
KeystorePassphrase | Encapsulates the actual passphrase to access the |
AlphanumericPassphrase | Encapsulates a passphrase consisting of letters and digits. |
RandomFile | Encapsulates the access to the |
The configuration of the encryption framework consists of the following aspects:
Location of the keystore
file
Location of the random
file
The strictMode
flag, see Concept - Encryption | The Strict Mode
Several other configuration values for the several instances of EncryptionConfig
These aspects are reflected in the GlobalEncryptionConfiguration
interfaces, which contain appropriate accessor methods in addition to the getConfigurationAccessor
method, which returns an instance of ConfigurationAccessor
that provides access to all remaining configuration values.
Main purpose of the strictMode
flag (intershop.encryption.strictMode.enabled
) is to ensure that PRD
environments do not use the PLAIN
encryption. Therefore, the strictMode
is enabled by default. An enabled strictMode
has the following consequences:
If the keystore
file is missing, the server start fails.
If the random
file is missing, the server start fails.
When trying to encrypt a value (com.intershop.common.encryption.capi.EncryptionManager#encryptString
) with algorithm=PLAIN
, the encryption fails with an exception.
On the other hand, a disabled strictMode
has the following consequences:
The server can start without the keystore
file and the random
file.
The server can start without the encryption.properties file.
The KeyManagerFactory
uses the NullKeystore.
A default EncryptionConfig
with algorithm=PLAIN
is used.
As a result, PRD
environments are forced to use proper encryption, and developers can omit the encryption configuration altogether.
The encryption framework supports 0 to n different encryption configs, which contain the following attributes:
Attribute | Description | Type | Mandatory/Optional |
---|---|---|---|
placeholder | Placeholder inside the keys of the properties | String | mandatory |
id | Name/ID of the encryption config | String | mandatory |
algorithm | Name of the encryption algorithm to use | String | optional, default= |
isDefault | If | Boolean | optional, default= |
The properties are then organized into groups of 1 to 3 property entries, all using the same placeholder and following the pattern:
intershop.encryption.$placeholder.(id|algorithm|isDefault)
Typically the placeholders are numeric values starting with 0
.
Example encryption configs properties:
#Intershop Encryption Configuration #Fri Apr 12 07:19:44 CEST 2024 intershop.encryption.0.id=encryption0 intershop.encryption.0.algorithm=PLAIN intershop.encryption.0.default=false intershop.encryption.1.id=verysave intershop.encryption.1.algorithm=PBEWithMD5AndTripleDES intershop.encryption.1.default=true intershop.encryption.2.id=verysave2 intershop.encryption.2.algorithm=PBEWithHmacSHA512AndAES_256 intershop.encryption.2.default=false intershop.encryption.3.id=demo intershop.encryption.3.algorithm=PLAIN intershop.encryption.3.default=false intershop.encryption.keystore.password=LGh31TxVaverusD1uQEqNdYgehjJFsF5KxcCndYuM1VBvKQ8N0f34KrrW0j2ZXcArb8DElhrzXi9AmZCPe35TvdMgR
There is the additional property intershop.encryption.keystore.password
, see Concept - Encryption | The Keystore-file for details.
The keys to be used for encryption and decryption are stored in a JCEKS keystore. This keystore and the keys within this keystore are encrypted using a passphrase. Therefore, this passphrase is required to open the keystore and look up the keys. The storage of this passphrase is divided into 4 parts:
The value of the property intershop.encryption.keystore.password
, see com.intershop.common.encryption.internal.KeystorePassphrase#externalPassphrase
Content of the random file
90 hard-coded indexes pointing to chars out of the random
file content
90 hard-coded chars
To create the above mentioned encryption configs, the keystore
file, and the random
file, a command line tool is provided as a Docker container. The corresponding Docker image is available at DockerHub. For a detailed description of how to use it, refer to Cookbook - Encryption.
For many use cases, encrypted data needs to be stored in a database, for example. This data needs to be decrypted later. So the encrypted data has to be stored in a way that it can be decrypted even if the default encryption config (see Concept - Encryption | Encryption Configs) has been changed. Therefore, the encrypted data contains the following information:
Information | Syntax | Mandatory/Optional |
---|---|---|
Encryption config ID | 1 to n chars out of:
| mandatory |
Algorithm name | 1 to n chars out of:
| mandatory |
Salt used during encryption (optional) | 1 to n chars out of (base64-alphabet):
| optional |
Actual data | 1 to n chars out of (base64-alphabet):
| mandatory |
Initialization vector used during encryption (optional) | 1 to n chars out of (base64-alphabet):
| optional |
The encryption data as a whole follows the pattern: <id>@<algorithm>:[<salt>|]<data>[|<ivSalt>]
During a typical encryption, see com.intershop.common.encryption.capi.EncryptionManager#encryptString
, the following steps are executed:
Look up default encryption config.
Look up the key that matches that encryption config.
Instantiate JCE-Cypher
matching that key (using generated salt and initialization vector).
Execute the actual encryption.
Render encrypted data, see Concept - Encryption | Encrypted Data Syntax.
During a typical decryption, see com.intershop.common.encryption.capi.EncryptionManager#decryptString
, the following steps are executed:
Parse the encrypted data, see Concept - Encryption | Encrypted Data Syntax.
Look up default encryption config using the ID from the encrypted data.
Look up the key that matches that encryption config.
Instantiate JCE-Cypher
matching that key (using salt and initialization vector from the encrypted data).
Execute the actual decryption.
During a typical encryption, see com.intershop.common.encryption.capi.EncryptionManager#reencryptString
, the following steps are executed:
Decrypt the data.
Encrypt the data (typically using a new default encryption config).
icm-as
)The code that uses the encryption library must use the methods of the com.intershop.common.encryption.capi.EncryptionManager
. The provided implementation of com.intershop.common.encryption.capi.EncryptionManager
is com.intershop.common.encryption.internal.EncryptionManagerImpl
, which has dependencies on:
EncryptionConfigs
EncryptionConstraints
KeyManager
CipherFactory
Therefore, the code you use must instantiate these components (in icm-as
, see com.intershop.beehive.core.internal.modules.EncryptionModule
). The most complex part is creating the KeyManager
, so this is implemented in com.intershop.common.encryption.internal.KeyManagerFactoryImpl
.
It actually executes the following steps:
If a keystore
file and a random
file exist: Return a com.intershop.common.encryption.internal.Keystore
that can load the keys from the keystore
file.
If either the keystore
file or the random
file is missing:
If strictMode
is enabled: Let the creation fail with a meaningful exception (make the icm-as
-startup fail).
If strictMode
is disabled: Return a com.intershop.common.encryption.internal.NullKeystore
that just is a no-op-implementation (allows to run the icm-as
without any encryption config).