-
Notifications
You must be signed in to change notification settings - Fork 157
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
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,10 +53,11 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// 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"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// 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"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private static final String ANDROID_KEY_STORE = "AndroidKeyStore"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private static final String ALGORITHM_RSA = "RSA"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -124,7 +125,8 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.setCertificateNotBefore(start.getTime()) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.setCertificateNotAfter(end.getTime()) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.setKeySize(RSA_KEY_SIZE) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA1) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.setBlockModes(KeyProperties.BLOCK_MODE_ECB) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.build(); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -355,6 +357,7 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Attempts to recover the existing AES Key or generates a new one if none is found. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Handles migration from PKCS1Padding-encrypted AES keys to OAEP-encrypted ones. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* @return a valid AES Key bytes | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* @throws IncompatibleDeviceException in the event the device can't understand the cryptographic settings required | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -366,39 +369,114 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (TextUtils.isEmpty(encodedEncryptedAES)) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
encodedEncryptedAES = storage.retrieveString(OLD_KEY_ALIAS); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte[] decryptedAESKey = null; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
boolean migrationNeeded = false; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (encodedEncryptedAES != null) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//Return existing key | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte[] encryptedAES = Base64.decode(encodedEncryptedAES, Base64.DEFAULT); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte[] existingAES = RSADecrypt(encryptedAES); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
final int aesExpectedLengthInBytes = AES_KEY_SIZE / 8; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//Prevent returning an 'Empty key' (invalid/corrupted) that was mistakenly saved | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (existingAES != null && existingAES.length == aesExpectedLengthInBytes) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//Key exists and has the right size | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return existingAES; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte[] encryptedAESBytes = Base64.decode(encodedEncryptedAES, Base64.DEFAULT); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Attempt 1: Decrypt with new OAEP configuration | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.d(TAG, "Attempting to decrypt AES key with OAEP (" + RSA_TRANSFORMATION + ")."); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
decryptedAESKey = RSADecrypt(encryptedAESBytes); // RSADecrypt uses the new RSA_TRANSFORMATION (OAEP) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.d(TAG, "AES key successfully decrypted with OAEP."); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}catch (IncompatibleDeviceException e){ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.w(TAG, "Failed to decrypt AES key with OAEP due to " + | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"IncompatibleDeviceException. Cause: " | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+ (e.getCause() != null ? e.getCause().getClass().getSimpleName() : "N/A") | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+ ". Attempting PKCS1 fallback for migration.", e); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Throwable cause = e.getCause(); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (cause instanceof InvalidKeyException) { // Specifically if key was not compatible with OAEP | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Attempt 2: Decrypt with old PKCS1Padding configuration | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.d(TAG, "Attempting to decrypt AES key with PKCS1Padding " + | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"("+ 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 commentThe reason will be displayed to describe this comment to others. Learn more. Semgrep identified an issue in your code: To resolve this comment: 🔧 No guidance has been designated for this issue. Fix according to your organization's approved methods. 💬 Ignore this findingReply with Semgrep commands to ignore this finding.
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 commentThe 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. 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 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
rsaPkcs1Cipher.init(Cipher.DECRYPT_MODE, rsaKeyEntry.getPrivateKey()); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
decryptedAESKey = rsaPkcs1Cipher.doFinal(encryptedAESBytes); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.i(TAG, "Successfully decrypted legacy AES key using PKCS1Padding. Migration is needed."); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
migrationNeeded = true; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (Exception pkcs1Exception) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.e(TAG, "Failed to decrypt AES key with PKCS1Padding fallback.", | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
pkcs1Exception); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
decryptedAESKey = null; // Ensure it's null after failed fallback | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Not an InvalidKeyException cause we're handling for PKCS1 fallback for IncompatibleDeviceException, so rethrow | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
throw e; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}catch (CryptoException e) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.w(TAG, "Failed to decrypt AES key with OAEP. Cause: " + | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(e.getCause() != null ? e.getCause().getClass().getSimpleName() : "N/A") + | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
". Attempting PKCS1 fallback for migration.", e); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Throwable cause = e.getCause(); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (cause instanceof BadPaddingException || cause instanceof | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
IllegalBlockSizeException || cause instanceof InvalidKeyException) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Attempt 2: Decrypt with old PKCS1Padding configuration | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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 commentThe reason will be displayed to describe this comment to others. Learn more. Semgrep identified an issue in your code: To resolve this comment: 🔧 No guidance has been designated for this issue. Fix according to your organization's approved methods. 💬 Ignore this findingReply with Semgrep commands to ignore this finding.
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 commentThe 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. 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 about 2 months ago To address the issue, the fallback decryption logic using The specific changes involve:
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.
Positive FeedbackNegative Feedback
Refresh and try again.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that was the PR which we are fixing to use OAEP padding. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this usage is intentional and strictly limited to a one-time data migration path for existing users. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
rsaPkcs1Cipher.init(Cipher.DECRYPT_MODE, rsaKeyEntry.getPrivateKey()); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
decryptedAESKey = rsaPkcs1Cipher.doFinal(encryptedAESBytes); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.i(TAG, "Successfully decrypted legacy AES key using PKCS1Padding." | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+ " Migration is needed."); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
migrationNeeded = true; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (Exception pkcs1Exception) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.e(TAG, "Failed to decrypt AES key with PKCS1Padding fallback." | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
, pkcs1Exception); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
decryptedAESKey = null; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Not a known migration-related exception cause from CryptoException, rethrow. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
throw e; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (decryptedAESKey != null) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
final int aesExpectedLengthInBytes = AES_KEY_SIZE / 8; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (decryptedAESKey.length != aesExpectedLengthInBytes) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.w(TAG, "Decrypted AES key has incorrect length (" + | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
decryptedAESKey.length + " bytes, expected " + aesExpectedLengthInBytes | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+ "). Discarding."); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
decryptedAESKey = null; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
migrationNeeded = false; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//Key doesn't exist. Generate new AES | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM_AES); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
keyGen.init(AES_KEY_SIZE); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte[] aes = keyGen.generateKey().getEncoded(); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//Save encrypted encoded version | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte[] encryptedAES = RSAEncrypt(aes); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
String encodedEncryptedAESText = new String(Base64.encode(encryptedAES, Base64.DEFAULT), StandardCharsets.UTF_8); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
storage.store(KEY_ALIAS, encodedEncryptedAESText); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return aes; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (NoSuchAlgorithmException e) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/* | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* This exceptions are safe to be ignored: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* - NoSuchAlgorithmException: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Thrown if the Algorithm implementation is not available. AES was introduced in API 1 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Read more in https://developer.android.com/reference/javax/crypto/KeyGenerator | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.e(TAG, "Error while creating the AES key.", e); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
throw new IncompatibleDeviceException(e); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (migrationNeeded && decryptedAESKey != null) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.d(TAG, "AES key was from legacy PKCS1. Deleting old RSA key pair to ensure new OAEP-compatible RSA key is used for re-encryption."); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
deleteRSAKeys(); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte[] encryptedAESWithOAEP = RSAEncrypt(decryptedAESKey); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
String newEncodedEncryptedAES = new String(Base64.encode(encryptedAESWithOAEP, Base64.DEFAULT), StandardCharsets.UTF_8); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
storage.store(KEY_ALIAS, newEncodedEncryptedAES); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.i(TAG, "AES key successfully migrated and re-encrypted with OAEP."); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (Exception reEncryptEx) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.e(TAG, "Failed to re-encrypt AES key with OAEP during migration. A new AES key will be generated.", reEncryptEx); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
decryptedAESKey = null; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (decryptedAESKey == null) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.d(TAG, "Generating new AES key."); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM_AES); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
keyGen.init(AES_KEY_SIZE); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
decryptedAESKey = keyGen.generateKey().getEncoded(); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
byte[] encryptedNewAES = RSAEncrypt(decryptedAESKey); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
String encodedEncryptedNewAESText = new String(Base64.encode(encryptedNewAES, Base64.DEFAULT), StandardCharsets.UTF_8); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
storage.store(KEY_ALIAS, encodedEncryptedNewAESText); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.d(TAG, "New AES key generated, encrypted with OAEP, and stored."); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (NoSuchAlgorithmException e) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Log.e(TAG, "Error while creating the new AES key.", e); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
throw new IncompatibleDeviceException(e); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return decryptedAESKey; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Check failure
Code scanning / CodeQL
Use of RSA algorithm without OAEP High
Copilot Autofix
AI 11 days ago
To address the issue, the code should avoid relying on
"RSA/ECB/PKCS1Padding"
by implementing a secure alternative for legacy key migration. One approach is to replace the PKCS1-padding-based decryption with a method that securely decrypts legacy keys while maintaining compatibility. Specifically, the migration process should involve re-encrypting legacy keys using OAEP padding or securely transitioning to OAEP-compatible keys.Changes needed:
OLD_PKCS1_RSA_TRANSFORMATION
with a secure migration strategy, ensuring that legacy keys are securely transitioned to OAEP-compatible encryption.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.
that was the PR which we are fixing to use OAEP padding.
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 usage is intentional and strictly limited to a one-time data migration path for existing users.
1>First, attempt to decrypt the stored AES key using the new, secure OAEP padding.
2>If that fails (specifically with a BadPaddingException or InvalidKeyException, which is expected for old keys), it then attempts a fallback decryption using the legacy PKCS1Padding.
3>If the legacy decryption succeeds, the key is immediately re-encrypted using the secure OAEP padding and re-saved to storage.