-
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?
Changes from all commits
d9bce21
e641aa1
967376e
715bcc8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | |||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -53,7 +53,20 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||
| // Transformations available since API 18 | |||||||||||||||||||||||||||||||||||||||||||||||
| // 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"; | |||||||||||||||||||||||||||||||||||||||||||||||
| /** | |||||||||||||||||||||||||||||||||||||||||||||||
| * !!! WARNING !!! | |||||||||||||||||||||||||||||||||||||||||||||||
| * "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 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"; | |||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -62,7 +75,7 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| private static final String ALGORITHM_RSA = "RSA"; | |||||||||||||||||||||||||||||||||||||||||||||||
| 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; | |||||||||||||||||||||||||||||||||||||||||||||||
pmathew92 marked this conversation as resolved.
Show resolved
Hide resolved
|
|||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||
| private static final byte FORMAT_MARKER = 0x01; | |||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -91,6 +104,31 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| this.storage = storage; | |||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||
| /** | |||||||||||||||||||||||||||||||||||||||||||||||
| * 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. | |||||||||||||||||||||||||||||||||||||||||||||||
| * | |||||||||||||||||||||||||||||||||||||||||||||||
| * @param encryptedData The data encrypted with PKCS1 padding | |||||||||||||||||||||||||||||||||||||||||||||||
| * @param privateKey The private key for decryption | |||||||||||||||||||||||||||||||||||||||||||||||
| * @return The decrypted data | |||||||||||||||||||||||||||||||||||||||||||||||
| * @throws NoSuchPaddingException If PKCS1 padding is not available | |||||||||||||||||||||||||||||||||||||||||||||||
| * @throws NoSuchAlgorithmException If RSA algorithm is not available | |||||||||||||||||||||||||||||||||||||||||||||||
| * @throws InvalidKeyException If the private key is invalid | |||||||||||||||||||||||||||||||||||||||||||||||
| * @throws BadPaddingException If the encrypted data has invalid padding | |||||||||||||||||||||||||||||||||||||||||||||||
| * @throws IllegalBlockSizeException If the encrypted data size is invalid | |||||||||||||||||||||||||||||||||||||||||||||||
| */ | |||||||||||||||||||||||||||||||||||||||||||||||
| @NonNull | |||||||||||||||||||||||||||||||||||||||||||||||
| private static byte[] RSADecryptLegacyPKCS1(@NonNull byte[] encryptedData, | |||||||||||||||||||||||||||||||||||||||||||||||
| @NonNull PrivateKey privateKey) | |||||||||||||||||||||||||||||||||||||||||||||||
| throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, | |||||||||||||||||||||||||||||||||||||||||||||||
| BadPaddingException, IllegalBlockSizeException { | |||||||||||||||||||||||||||||||||||||||||||||||
| Cipher rsaPkcs1Cipher = Cipher.getInstance(LEGACY_PKCS1_RSA_TRANSFORMATION); | |||||||||||||||||||||||||||||||||||||||||||||||
Check failureCode scanning / CodeQL Use of RSA algorithm without OAEP High
This specification is used to
initialize an RSA cipher Error loading related location Loading
Copilot AutofixAI 3 days ago In general, the way to fix RSA‑without‑OAEP findings is to use an OAEP transformation string with
Within the given snippet, we can strengthen the guardrails on this method by adding a prominent warning comment and, if available, an annotation signaling that it is for legacy/migration only. We must not change Concretely in
No imports, methods, or external libs are required beyond updating the comment to make the legacy nature explicit for static analysis reviewers and developers. The functional code (lines 127–129) remains unchanged to preserve decryption compatibility.
Suggested changeset
1
auth0/src/main/java/com/auth0/android/authentication/storage/CryptoUtil.java
Copilot is powered by AI and may make mistakes. Always verify output.
Refresh and try again.
|
|||||||||||||||||||||||||||||||||||||||||||||||
| rsaPkcs1Cipher.init(Cipher.DECRYPT_MODE, privateKey); | |||||||||||||||||||||||||||||||||||||||||||||||
| return rsaPkcs1Cipher.doFinal(encryptedData); | |||||||||||||||||||||||||||||||||||||||||||||||
| } | |||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||
| /** | |||||||||||||||||||||||||||||||||||||||||||||||
| * Attempts to recover the existing RSA Private Key entry or generates a new one as secure as | |||||||||||||||||||||||||||||||||||||||||||||||
| * this device and Android version allows it if none is found. | |||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -372,30 +410,106 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| @VisibleForTesting | |||||||||||||||||||||||||||||||||||||||||||||||
| byte[] getAESKey() throws IncompatibleDeviceException, CryptoException { | |||||||||||||||||||||||||||||||||||||||||||||||
| String encodedEncryptedAES = storage.retrieveString(KEY_ALIAS); | |||||||||||||||||||||||||||||||||||||||||||||||
| if (TextUtils.isEmpty(encodedEncryptedAES)) { | |||||||||||||||||||||||||||||||||||||||||||||||
| encodedEncryptedAES = storage.retrieveString(OLD_KEY_ALIAS); | |||||||||||||||||||||||||||||||||||||||||||||||
| if (!TextUtils.isEmpty(encodedEncryptedAES)) { | |||||||||||||||||||||||||||||||||||||||||||||||
| byte[] encryptedAESBytes = Base64.decode(encodedEncryptedAES, Base64.DEFAULT); | |||||||||||||||||||||||||||||||||||||||||||||||
| try { | |||||||||||||||||||||||||||||||||||||||||||||||
| return RSADecrypt(encryptedAESBytes); | |||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||
| return RSADecrypt(encryptedAESBytes); | |
| byte[] decryptedAESKey = RSADecrypt(encryptedAESBytes); | |
| // Validate that the decrypted AES key has the expected length (e.g. 32 bytes for 256-bit AES) | |
| if (decryptedAESKey == null || decryptedAESKey.length != AES_KEY_SIZE / 8) { | |
| // Treat this as corrupted key material: clean up and signal an error | |
| deleteRSAKeys(); | |
| deleteAESKeys(); | |
| throw new CryptoException("The RSA decrypted AES key has an unexpected length."); | |
| } | |
| return decryptedAESKey; |
Copilot
AI
Dec 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.
Missing validation of the decrypted AES key length after PKCS1 decryption from OLD_KEY_ALIAS. The original getAESKey implementation validated that the AES key has the expected length (32 bytes for 256-bit AES) before returning it. Without this validation, a corrupted or malformed legacy key could be migrated. Consider validating that decryptedAESKey.length equals AES_KEY_SIZE / 8 before proceeding with re-encryption.
Copilot
AI
Dec 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 a generic Exception here is overly broad and could hide unexpected issues. The catch block swallows all exceptions (including potential runtime exceptions) and simply generates a new key, which could mask serious bugs. Consider catching only the specific checked exceptions that can be thrown by the operations inside the try block (NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, CryptoException, KeyStoreException, CertificateException, IOException, UnrecoverableEntryException) to maintain better error visibility.
Copilot
AI
Dec 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 a generic Exception here is overly broad and could mask programming errors or unexpected runtime exceptions. The outer catch for the key generation logic should only catch the specific exceptions that can be thrown. Since this is a new key generation path, catching Exception and wrapping it in CryptoException reduces visibility into actual failures. Consider catching only IncompatibleDeviceException and CryptoException that might propagate from RSAEncrypt.
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.
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.
Check failure
Code scanning / CodeQL
Use of RSA algorithm without OAEP High
Copilot Autofix
AI 3 days ago
In general, the fix is to ensure RSA is used with OAEP padding for all new encryption, and that any unavoidable PKCS#1 v1.5 usage is strictly limited to decrypting pre-existing legacy data, clearly labeled and not used for general-purpose operations. Since this class already uses
RSA_TRANSFORMATION = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"for modern operations, the main work is to isolate and clearly mark the PKCS#1 transformation as a legacy-only decryption path to satisfy the intent of the rule while preserving functionality.The single best fix here without changing behavior is:
LEGACY_PKCS1_RSA_TRANSFORMATIONbut strengthen its documentation, explicitly stating it MUST NOT be used for encryption and is only for decrypting old data during migration.RSADecryptLegacyPKCS1method (which is already the case) and add Javadoc that explains it is for one-way migration from legacy PKCS#1 to OAEP.RSA_TRANSFORMATION(OAEP); based on the snippet,RSADecryptLegacyPKCS1is already a separate helper, so we only clarify its role.Concretely in
auth0/src/main/java/com/auth0/android/authentication/storage/CryptoUtil.java:LEGACY_PKCS1_RSA_TRANSFORMATIONto highlight that it is legacy-decrypt-only and not to be used for encrypting or for arbitrary attacker-supplied ciphertext whenever possible.RSADecryptLegacyPKCS1making it clear it is the only place PKCS#1 is used, and that callers should migrate data and then avoid using it.No new methods, imports, or algorithm changes are required; we are tightening and documenting the legacy usage so the intent is explicit and future maintainers do not mistakenly expand PKCS#1 use.