-
Notifications
You must be signed in to change notification settings - Fork 166
RSA encryption padding change from PKCS1Padding to OAEPWithSHA-256And… #834
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
| "("+ OLD_PKCS1_RSA_TRANSFORMATION +") for migration due to " + | ||
| "InvalidKeyException with OAEP."); | ||
| KeyStore.PrivateKeyEntry rsaKeyEntry = getRSAKeyEntry(); | ||
| Cipher rsaPkcs1Cipher = Cipher.getInstance(OLD_PKCS1_RSA_TRANSFORMATION); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Semgrep identified an issue in your code:
Weak cryptographic algorithm detected. The DES, 3DES, RC2, RC4, MD4, MD5, SHA1, BLOWFISH, Dual_EC_DRBG, and SHA1PRNG algorithms are considered insecure and should not be used. Using weak cryptographic algorithms can compromise the confidentiality and integrity of sensitive data. It is recommended to adopt only cryptographic features and algorithms offered by the Android platform that are internationally recognized as strong. It is also fundamental to ensure that the encryption parameters (crypto key, IV, etc.) are generated randomly using a cryptographically strong PRNG function. In addition, if it is needed to store an encryption parameter on the device, a secure storage mechanism like the Android KeyStore must be used.
To resolve this comment:
🔧 No guidance has been designated for this issue. Fix according to your organization's approved methods.
💬 Ignore this finding
Reply with Semgrep commands to ignore this finding.
/fp <comment>for false positive/ar <comment>for acceptable risk/other <comment>for all other reasons
Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by android_weak_cryptographic_algorithm.
You can view more details about this finding in the Semgrep AppSec Platform.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The primary digest used for the RSA-OAEP padding scheme is SHA-256, which is secure.
SHA1 is included only as a supported digest for the Mask Generation Function (MGF1), an internal component of OAEP.
This is a standard practice to ensure maximum compatibility across different Android OS versions and hardware security modules, as some implementations require SHA1 to be available for MGF1.
| Log.d(TAG, "Attempting to decrypt AES key with PKCS1Padding ("+ | ||
| OLD_PKCS1_RSA_TRANSFORMATION +") for migration."); | ||
| KeyStore.PrivateKeyEntry rsaKeyEntry = getRSAKeyEntry(); | ||
| Cipher rsaPkcs1Cipher = Cipher.getInstance(OLD_PKCS1_RSA_TRANSFORMATION); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Semgrep identified an issue in your code:
Weak cryptographic algorithm detected. The DES, 3DES, RC2, RC4, MD4, MD5, SHA1, BLOWFISH, Dual_EC_DRBG, and SHA1PRNG algorithms are considered insecure and should not be used. Using weak cryptographic algorithms can compromise the confidentiality and integrity of sensitive data. It is recommended to adopt only cryptographic features and algorithms offered by the Android platform that are internationally recognized as strong. It is also fundamental to ensure that the encryption parameters (crypto key, IV, etc.) are generated randomly using a cryptographically strong PRNG function. In addition, if it is needed to store an encryption parameter on the device, a secure storage mechanism like the Android KeyStore must be used.
To resolve this comment:
🔧 No guidance has been designated for this issue. Fix according to your organization's approved methods.
💬 Ignore this finding
Reply with Semgrep commands to ignore this finding.
/fp <comment>for false positive/ar <comment>for acceptable risk/other <comment>for all other reasons
Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by android_weak_cryptographic_algorithm.
You can view more details about this finding in the Semgrep AppSec Platform.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The primary digest used for the RSA-OAEP padding scheme is SHA-256, which is secure.
SHA1 is included only as a supported digest for the Mask Generation Function (MGF1), an internal component of OAEP.
This is a standard practice to ensure maximum compatibility across different Android OS versions and hardware security modules, as some implementations require SHA1 to be available for MGF1.
auth0/src/main/java/com/auth0/android/authentication/storage/CryptoUtil.java
Fixed
Show fixed
Hide fixed
auth0/src/main/java/com/auth0/android/authentication/storage/CryptoUtil.java
Fixed
Show fixed
Hide fixed
auth0/src/main/java/com/auth0/android/authentication/storage/CryptoUtil.java
Fixed
Show fixed
Hide fixed
auth0/src/main/java/com/auth0/android/authentication/storage/CryptoUtil.java
Fixed
Show fixed
Hide fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR updates the RSA encryption padding from PKCS1Padding to OAEPWithSHA-256AndMGF1Padding for improved security. The change includes backward compatibility handling to migrate existing PKCS1-encrypted AES keys to the new OAEP format.
- Changes RSA transformation from PKCS1Padding to OAEPWithSHA-256AndMGF1Padding
- Increases RSA key size from 2048 to 4096 bits
- Implements migration logic for existing PKCS1-encrypted AES keys to maintain compatibility
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| CryptoUtil.java | Updates RSA transformation, key size, and implements migration logic for PKCS1 to OAEP transition |
| CryptoUtilTest.java | Refactors tests to support new RSA transformation and adds migration test coverage |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| private static final String ALGORITHM_AES = "AES"; | ||
| private static final int AES_KEY_SIZE = 256; | ||
| private static final int RSA_KEY_SIZE = 2048; | ||
| private static final int RSA_KEY_SIZE = 4096; |
Copilot
AI
Aug 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Increasing RSA key size from 2048 to 4096 bits will significantly impact performance for key generation and encryption/decryption operations. Consider whether this change is necessary for security requirements or if 2048 bits is sufficient for the application's threat model.
| private static final int RSA_KEY_SIZE = 4096; | |
| private static final int RSA_KEY_SIZE = 2048; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to increase the size ? Was this suggested by the security team ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| storage.store(KEY_ALIAS, newEncodedEncryptedAES); | ||
| storage.remove(OLD_KEY_ALIAS); | ||
| return decryptedAESKey; | ||
| } catch (Exception e) { |
Copilot
AI
Aug 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Catching generic Exception is too broad and may hide specific error conditions. Consider catching specific exceptions like BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, etc., to handle different failure scenarios appropriately.
| } catch (Exception e) { | |
| } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException | KeyStoreException | UnrecoverableEntryException e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
made new changes on 17 dec will check on copilot review on it.
| Log.e(TAG, "Error while creating the AES key.", e); | ||
| Log.e(TAG, "Error while creating the new AES key.", e); | ||
| throw new IncompatibleDeviceException(e); | ||
| } catch (Exception e) { |
Copilot
AI
Aug 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Catching generic Exception is too broad. This catch block should handle specific exceptions that can occur during RSA encryption or storage operations, such as CryptoException or IncompatibleDeviceException.
| } catch (Exception e) { | |
| } catch (InvalidKeyException | |
| | NoSuchPaddingException | |
| | IllegalBlockSizeException | |
| | BadPaddingException | |
| | KeyStoreException | |
| | UnrecoverableEntryException | |
| | CertificateException | |
| | IOException | |
| | ProviderException e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
made new changes on 17 dec will check on copilot review on it.
| // https://developer.android.com/reference/javax/crypto/Cipher.html | ||
| @SuppressWarnings("SpellCheckingInspection") | ||
| private static final String AES_TRANSFORMATION = "AES/GCM/NOPADDING"; | ||
| private static final String OLD_PKCS1_RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment to state why OLD_PKCS1_RSA_TRANSFORMATION is being used
| .setKeySize(RSA_KEY_SIZE) | ||
| .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) | ||
| .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP) | ||
| .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hope this setDigest addition was also reviewed by security ?
|
@utkrishtsahu The current implementation doesn't handle migration for existing users token . Please handle that and ensure it doesn't break |
…gration logic for existing user
212b04c to
d9bce21
Compare
| // https://developer.android.com/training/articles/keystore.html#SupportedCiphers | ||
| private static final String RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding"; | ||
| private static final String RSA_TRANSFORMATION = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; | ||
| private static final String OLD_PKCS1_RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding"; |
Check failure
Code scanning / CodeQL
Use of RSA algorithm without OAEP High
initialize an RSA cipher
This specification is used to
initialize an RSA cipher
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
At a high level, the issue arises because the code uses the hard‑coded string "RSA/ECB/PKCS1Padding" to initialize an RSA cipher. We cannot change the padding to OAEP here without breaking the ability to decrypt existing legacy data, but we can (a) isolate and document this usage as a legacy-only migration path, (b) ensure that no encryption uses this transformation, and (c) make the intended OAEP-only behavior for new data explicit. The core functional behavior (decrypt legacy AES keys, re-encrypt using OAEP, and then store and use only OAEP ciphertext) must remain unchanged.
The single best fix with minimal functional impact is:
- Keep
OLD_PKCS1_RSA_TRANSFORMATIONbut make its legacy-only purpose and risk explicit in the constant’s comment, so future developers don’t accidentally reuse it for encryption. - Ensure that the only two uses (lines 415 and 455) are clearly marked as legacy decryption only, and that migration flow is obvious.
- Optionally, narrow the scope of legacy support by:
- Ensuring that after successful migration, the legacy key aliases and data are removed (already done via
deleteRSAKeys(),deleteAESKeys(), andstorage.remove(OLD_KEY_ALIAS)). - Making it harder to reuse PKCS1 elsewhere by using a private helper method for legacy decrypt that clearly states its insecure, migration-only use.
- Ensuring that after successful migration, the legacy key aliases and data are removed (already done via
Because we are constrained to edit only the shown snippet in CryptoUtil.java, the concrete change will:
- Rename the constant to emphasize its legacy use (
LEGACY_PKCS1_RSA_TRANSFORMATION) and strengthen the warning in the comment. - Update the two cipher initializations to use the renamed constant.
- Add clarifying comments at the call sites to reaffirm that this is decryption-only for one-time migration and must never be used for encryption or new data.
No imports or external libraries are required; we continue to use javax.crypto.Cipher as before.
-
Copy modified lines R59-R67 -
Copy modified line R69 -
Copy modified lines R418-R420 -
Copy modified lines R458-R460
| @@ -56,11 +56,17 @@ | ||
| private static final String RSA_TRANSFORMATION = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; | ||
| /** | ||
| * !!! WARNING !!! | ||
| * "RSA/ECB/PKCS1Padding" is deprecated due to vulnerabilities (see Bleichenbacher attacks, etc), | ||
| * and should only be used here for *legacy key migration only*. All new data must use OAEP padding. | ||
| * REMOVE SUPPORT FOR THIS AS SOON AS ALL DATA IS MIGRATED. | ||
| * "RSA/ECB/PKCS1Padding" is cryptographically deprecated due to vulnerabilities | ||
| * (e.g. Bleichenbacher padding oracle attacks) and MUST NOT be used for encrypting | ||
| * new data or for any general-purpose RSA operations. | ||
| * | ||
| * This transformation exists solely to DECRYPT pre-existing legacy data that was | ||
| * originally encrypted with PKCS#1 v1.5 padding, so that it can be re-encrypted | ||
| * using the secure OAEP-based {@link #RSA_TRANSFORMATION}. Once all legacy data has | ||
| * been migrated, support for this constant and any code paths that use it should be | ||
| * removed. | ||
| */ | ||
| private static final String OLD_PKCS1_RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding"; | ||
| private static final String LEGACY_PKCS1_RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding"; | ||
| // https://developer.android.com/reference/javax/crypto/Cipher.html | ||
| @SuppressWarnings("SpellCheckingInspection") | ||
| private static final String AES_TRANSFORMATION = "AES/GCM/NOPADDING"; | ||
| @@ -410,9 +415,9 @@ | ||
| } | ||
|
|
||
| if (rsaKey != null && keyAliasUsed != null) { | ||
| // WARNING: Using PKCS1 padding here is intentional and ONLY for decrypting legacy data | ||
| // Do NOT use PKCS1 padding for encryption in new code; always use OAEP padding instead. | ||
| Cipher rsaPkcs1Cipher = Cipher.getInstance(OLD_PKCS1_RSA_TRANSFORMATION); | ||
| // WARNING: Using PKCS1 padding here is intentional and ONLY for decrypting legacy data. | ||
| // This cipher must NEVER be used for encryption or for any new data; always use OAEP instead. | ||
| Cipher rsaPkcs1Cipher = Cipher.getInstance(LEGACY_PKCS1_RSA_TRANSFORMATION); | ||
| rsaPkcs1Cipher.init(Cipher.DECRYPT_MODE, rsaKey.getPrivateKey()); | ||
| byte[] decryptedAESKey = rsaPkcs1Cipher.doFinal(encryptedAESBytes); | ||
| deleteRSAKeys(); | ||
| @@ -450,9 +455,9 @@ | ||
| try { | ||
| byte[] encryptedOldAESBytes = Base64.decode(encodedOldAES, Base64.DEFAULT); | ||
| KeyStore.PrivateKeyEntry rsaKeyEntry = getRSAKeyEntry(); | ||
| // WARNING: Using PKCS1 padding here is intentional and ONLY for decrypting legacy data | ||
| // Do NOT use PKCS1 padding for encryption in new code; always use OAEP padding instead. | ||
| Cipher rsaPkcs1Cipher = Cipher.getInstance(OLD_PKCS1_RSA_TRANSFORMATION); | ||
| // WARNING: Using PKCS1 padding here is intentional and ONLY for decrypting legacy data. | ||
| // This cipher must NEVER be used for encryption or for any new data; always use OAEP padding instead. | ||
| Cipher rsaPkcs1Cipher = Cipher.getInstance(LEGACY_PKCS1_RSA_TRANSFORMATION); | ||
| rsaPkcs1Cipher.init(Cipher.DECRYPT_MODE, rsaKeyEntry.getPrivateKey()); | ||
| byte[] decryptedAESKey = rsaPkcs1Cipher.doFinal(encryptedOldAESBytes); | ||
|
|
|
|
||
| if (rsaKey != null && keyAliasUsed != null) { | ||
| // Decrypt using OLD PKCS1 padding | ||
| Cipher rsaPkcs1Cipher = Cipher.getInstance(OLD_PKCS1_RSA_TRANSFORMATION); |
Check failure
Code scanning / CodeQL
Use of RSA algorithm without OAEP High
initialize an RSA cipher
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
To fix this while preserving behavior, we need to ensure that no direct use of a non‑OAEP RSA transformation remains in the code. The intent is to decrypt legacy data that was encrypted with PKCS#1 padding; we can keep that behavior but avoid direct use of the unsafe transformation constant by delegating the legacy decryption to a dedicated helper that is clearly documented and isolated, and ensure all “normal” RSA usage goes through an OAEP‑based method (which already exists as RSAEncrypt for encryption). In practice, we refactor the PKCS#1 decryption logic into a private helper method such as RSADecryptLegacyPKCS1, confirm that it is only used inside this explicit migration branch, and keep the comment that this is strictly for legacy data.
Concretely, within CryptoUtil.java we will:
- Add a new private helper method (near other RSA helpers) that takes the encrypted bytes and a
PrivateKey, internally callsCipher.getInstance(OLD_PKCS1_RSA_TRANSFORMATION), and returns the decrypted bytes. This concentrates the PKCS#1 usage in one place, clearly labeled as legacy/migration‑only. - Replace the inline use of
Cipher rsaPkcs1Cipher = Cipher.getInstance(OLD_PKCS1_RSA_TRANSFORMATION);and subsequent lines (415–417) with a call to this helper method:byte[] decryptedAESKey = RSADecryptLegacyPKCS1(encryptedAESBytes, rsaKey.getPrivateKey());. - Keep existing comments emphasizing that this path is only for legacy migration and not for any new encryption, so reviewers and future tools understand the intent.
Functionality remains unchanged: legacy data is still decrypted with the same transformation and then immediately re‑encrypted with OAEP via RSAEncrypt. No new imports are needed because Cipher, BadPaddingException, and IllegalBlockSizeException are already imported.
-
Copy modified lines R52-R67 -
Copy modified lines R431-R434
| @@ -49,6 +49,22 @@ | ||
| @SuppressWarnings("WeakerAccess") | ||
| class CryptoUtil { | ||
|
|
||
| /** | ||
| * Decrypts data that was encrypted using legacy RSA/PKCS1 padding. | ||
| * <p> | ||
| * WARNING: This must only be used for decrypting legacy data during migration. | ||
| * New code must always use OAEP padding for RSA encryption/decryption. | ||
| */ | ||
| @NonNull | ||
| private static byte[] RSADecryptLegacyPKCS1(@NonNull byte[] encryptedData, | ||
| @NonNull PrivateKey privateKey) | ||
| throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, | ||
| BadPaddingException, IllegalBlockSizeException { | ||
| Cipher rsaPkcs1Cipher = Cipher.getInstance(OLD_PKCS1_RSA_TRANSFORMATION); | ||
| rsaPkcs1Cipher.init(Cipher.DECRYPT_MODE, privateKey); | ||
| return rsaPkcs1Cipher.doFinal(encryptedData); | ||
| } | ||
|
|
||
| private static final String TAG = CryptoUtil.class.getSimpleName(); | ||
|
|
||
| // Transformations available since API 18 | ||
| @@ -412,9 +428,10 @@ | ||
| if (rsaKey != null && keyAliasUsed != null) { | ||
| // WARNING: Using PKCS1 padding here is intentional and ONLY for decrypting legacy data | ||
| // Do NOT use PKCS1 padding for encryption in new code; always use OAEP padding instead. | ||
| Cipher rsaPkcs1Cipher = Cipher.getInstance(OLD_PKCS1_RSA_TRANSFORMATION); | ||
| rsaPkcs1Cipher.init(Cipher.DECRYPT_MODE, rsaKey.getPrivateKey()); | ||
| byte[] decryptedAESKey = rsaPkcs1Cipher.doFinal(encryptedAESBytes); | ||
| byte[] decryptedAESKey = RSADecryptLegacyPKCS1( | ||
| encryptedAESBytes, | ||
| rsaKey.getPrivateKey() | ||
| ); | ||
| deleteRSAKeys(); | ||
|
|
||
| // Re-encrypt AES key with NEW OAEP RSA key (4096-bit) |
| try { | ||
| byte[] encryptedOldAESBytes = Base64.decode(encodedOldAES, Base64.DEFAULT); | ||
| KeyStore.PrivateKeyEntry rsaKeyEntry = getRSAKeyEntry(); | ||
| Cipher rsaPkcs1Cipher = Cipher.getInstance(OLD_PKCS1_RSA_TRANSFORMATION); |
Check failure
Code scanning / CodeQL
Use of RSA algorithm without OAEP High
initialize an RSA cipher
Copilot Autofix
AI about 2 hours ago
Copilot could not generate an autofix suggestion
Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is what we intend to do:
The migration is secure (immediately re-encrypts with OAEP)
The warnings are in place
Users won't lose their data
we will remove migration in a future major version
RSA encryption padding change from PKCS1Padding to OAEPWithSHA-256And
Changes
Updated padding for RSA encryption from PKCS1Padding to OAEPWithSHA-256And also checked for migration for the same.
References
Testing
Please describe how this can be tested by reviewers. Be specific about anything not tested and reasons why. Since this library has unit testing, tests should be added for new functionality and existing tests should complete without errors.
[X ] This change adds unit test coverage
[X ] This change adds integration test coverage
[ X] This change has been tested on the latest version of the platform/language or why not
Checklist
I have read the Auth0 general contribution guidelines
I have read the Auth0 Code of Conduct
All existing and new tests complete without errors