Background
In the DPDP Accelerator Event Notification component, subscriptions for webhook dispatch and poll delivery configure a sharedSecret. This secret is used for:
- Generating HMAC-SHA256 signatures (
event-signature header) on outgoing webhook deliveries.
- Authenticating incoming polling requests (
event-signature header).
Currently, sharedSecret is persisted in plain text directly into the SHARED_SECRET column of the DPDP_SUBSCRIPTION database table.
In contrast, WSO2 Identity Server (IS) protects sensitive credentials stored in relational tables (such as OAuth consumer secrets in IDN_OAUTH_CONSUMER_APPS) using keystore-backed reversible encryption via Carbon's CryptoUtil (encryptAndBase64Encode / base64DecodeAndDecrypt). Because sharedSecret must be read at runtime to calculate and verify HMAC digests, it cannot be one-way hashed and must follow this same reversible encryption approach.
Problem Statement
Storing sharedSecret in plain text exposes sensitive cryptographic material at rest in the database. Anyone with read access to the database or database backups can extract the shared secrets and forge webhook signatures or poll events unauthorized.
Proposed Solution
Adopt the standard WSO2 IS reversible encryption mechanism using org.wso2.carbon.core.util.CryptoUtil to encrypt subscription shared secrets before persisting them, and decrypt them upon retrieval.
-
Cryptographic Engine:
- Use
CryptoUtil.getDefaultCryptoUtil() backed by the Carbon server's primary/internal keystore ([keystore.internal] / [keystore.primary]):
- Encrypt:
CryptoUtil.getDefaultCryptoUtil().encryptAndBase64Encode(plainText.getBytes(StandardCharsets.UTF_8))
- Decrypt:
new String(CryptoUtil.getDefaultCryptoUtil().base64DecodeAndDecrypt(cipherText), StandardCharsets.UTF_8)
-
Persistence Layer Integration:
- Write Path (SubscriptionDAOImpl): Encrypt
sharedSecret before binding it to INSERT and UPDATE SQL statements.
- Read Path (SubscriptionDAOImpl & DeliveryDAOImpl): Decrypt
SHARED_SECRET when constructing Subscription and Delivery domain objects so business logic (WebhookDeliveryTask, PollDeliveryService, HmacSigner) continues to work with plain-text secrets seamlessly in memory.
-
Graceful Backward Compatibility:
- Ensure a fallback mechanism: If
CryptoException occurs during decryption (e.g. for existing unencrypted database records), fall back to treating the stored value as plain text.
Scope of Changes
org.wso2.dpdp.accelerator.common (or relevant module):
- Provide a reusable encryption helper wrapping
CryptoUtil with error handling and fallback logic.
org.wso2.dpdp.accelerator.event.notifications.dao:
- Update
SubscriptionDAOImpl (insert, update, and get methods).
- Update
DeliveryDAOImpl (queries populating delivery subscription context).
- Unit and Integration Tests:
- Add unit tests validating encryption on save, decryption on read, error handling, and plain-text fallback.
- Verify existing webhook dispatch and polling tests continue to pass.
Acceptance Criteria
Background
In the DPDP Accelerator Event Notification component, subscriptions for webhook dispatch and poll delivery configure a
sharedSecret. This secret is used for:event-signatureheader) on outgoing webhook deliveries.event-signatureheader).Currently,
sharedSecretis persisted in plain text directly into theSHARED_SECRETcolumn of theDPDP_SUBSCRIPTIONdatabase table.In contrast, WSO2 Identity Server (IS) protects sensitive credentials stored in relational tables (such as OAuth consumer secrets in
IDN_OAUTH_CONSUMER_APPS) using keystore-backed reversible encryption via Carbon'sCryptoUtil(encryptAndBase64Encode/base64DecodeAndDecrypt). BecausesharedSecretmust be read at runtime to calculate and verify HMAC digests, it cannot be one-way hashed and must follow this same reversible encryption approach.Problem Statement
Storing
sharedSecretin plain text exposes sensitive cryptographic material at rest in the database. Anyone with read access to the database or database backups can extract the shared secrets and forge webhook signatures or poll events unauthorized.Proposed Solution
Adopt the standard WSO2 IS reversible encryption mechanism using
org.wso2.carbon.core.util.CryptoUtilto encrypt subscription shared secrets before persisting them, and decrypt them upon retrieval.Cryptographic Engine:
CryptoUtil.getDefaultCryptoUtil()backed by the Carbon server's primary/internal keystore ([keystore.internal]/[keystore.primary]):CryptoUtil.getDefaultCryptoUtil().encryptAndBase64Encode(plainText.getBytes(StandardCharsets.UTF_8))new String(CryptoUtil.getDefaultCryptoUtil().base64DecodeAndDecrypt(cipherText), StandardCharsets.UTF_8)Persistence Layer Integration:
sharedSecretbefore binding it toINSERTandUPDATESQL statements.SHARED_SECRETwhen constructingSubscriptionandDeliverydomain objects so business logic (WebhookDeliveryTask,PollDeliveryService,HmacSigner) continues to work with plain-text secrets seamlessly in memory.Graceful Backward Compatibility:
CryptoExceptionoccurs during decryption (e.g. for existing unencrypted database records), fall back to treating the stored value as plain text.Scope of Changes
org.wso2.dpdp.accelerator.common(or relevant module):CryptoUtilwith error handling and fallback logic.org.wso2.dpdp.accelerator.event.notifications.dao:SubscriptionDAOImpl(insert, update, and get methods).DeliveryDAOImpl(queries populating delivery subscription context).Acceptance Criteria
sharedSecretis stored encrypted (Base64-encoded ciphertext) inDPDP_SUBSCRIPTION.SHARED_SECRET.