Skip to content

Commit 64212a1

Browse files
committed
Merge pull request #2749 from wmathurin/unexpected_logout
Fix user logout issue during V11→V13 app upgrades caused by failed OAEP re-encryption
1 parent a5b41de commit 64212a1

File tree

3 files changed

+59
-0
lines changed

3 files changed

+59
-0
lines changed

libs/SalesforceSDK/src/com/salesforce/androidsdk/security/KeyStoreWrapper.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@
2828

2929
import android.os.Build;
3030
import android.security.keystore.KeyGenParameterSpec;
31+
import android.security.keystore.KeyInfo;
3132
import android.security.keystore.KeyProperties;
3233
import android.util.Base64;
3334

3435
import com.salesforce.androidsdk.util.SalesforceSDKLogger;
3536

3637
import java.io.IOException;
38+
import java.security.KeyFactory;
3739
import java.security.KeyPairGenerator;
3840
import java.security.KeyStore;
3941
import java.security.KeyStoreException;
@@ -253,6 +255,34 @@ private synchronized void createKeysIfNecessary(String algorithm, String name, i
253255
}
254256
}
255257

258+
/**
259+
* Checks if the RSA key supports OAEP encryption padding.
260+
*
261+
* @param name Alias of the key to check.
262+
* @return true if the key supports OAEP padding, false otherwise.
263+
*/
264+
public boolean keySupportsOAEPPadding(String name) {
265+
try {
266+
if (keyStore.containsAlias(name)) {
267+
PrivateKey privateKey = (PrivateKey) keyStore.getKey(name, null);
268+
KeyFactory keyFactory = KeyFactory.getInstance(privateKey.getAlgorithm(), ANDROID_KEYSTORE);
269+
KeyInfo keyInfo = keyFactory.getKeySpec(privateKey, KeyInfo.class);
270+
String[] encryptionPaddings = keyInfo.getEncryptionPaddings();
271+
272+
if (encryptionPaddings != null) {
273+
for (String padding : encryptionPaddings) {
274+
if (KeyProperties.ENCRYPTION_PADDING_RSA_OAEP.equals(padding)) {
275+
return true;
276+
}
277+
}
278+
}
279+
}
280+
} catch (Exception e) {
281+
SalesforceSDKLogger.e(TAG, "Could not check key padding capabilities", e);
282+
}
283+
return false;
284+
}
285+
256286
// For testing only - create key the way we used to before the 11.1.1 cipher change
257287
synchronized void legacyCreateKeysIfNecessary(String algorithm, String name, int length) {
258288
try {

libs/SalesforceSDK/src/com/salesforce/androidsdk/security/SalesforceKeyGenerator.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,12 @@ private synchronized static String generateUniqueIdIfNoneStored(String name, int
176176

177177
// Encrypt and store unique id if it was just created, or if it had to be decrypted with old cipher mode
178178
if (storeUniqueId) {
179+
// Check if existing key supports OAEP padding, recreate if not
180+
if (!KeyStoreWrapper.getInstance().keySupportsOAEPPadding(KEYSTORE_ALIAS)) {
181+
SalesforceSDKLogger.i(TAG, "Key doesn't support OAEP padding, recreating key pair with OAEP support");
182+
KeyStoreWrapper.getInstance().deleteKey(KEYSTORE_ALIAS);
183+
}
184+
179185
final PublicKey publicKey = KeyStoreWrapper.getInstance().getRSAPublicKey(KEYSTORE_ALIAS);
180186
final String encryptedKey = Encryptor.encryptWithRSA(publicKey, uniqueId, Encryptor.CipherMode.RSA_OAEP_SHA256);
181187
storeInSharedPrefs(ID_PREFIX + name, encryptedKey);

libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/security/KeyStoreWrapperTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public class KeyStoreWrapperTest {
5858

5959
private static final String KEY_1 = "key_1";
6060
private static final String KEY_2 = "key_2";
61+
private static final String KEY_OAEP_TEST = "key_oaep_test";
6162
private static final int RSA_LENGTH = 2048;
6263

6364
@Before
@@ -72,6 +73,7 @@ public void tearDown() throws Exception {
7273
final KeyStoreWrapper keyStoreWrapper = KeyStoreWrapper.getInstance();
7374
keyStoreWrapper.deleteKey(KEY_1);
7475
keyStoreWrapper.deleteKey(KEY_2);
76+
keyStoreWrapper.deleteKey(KEY_OAEP_TEST);
7577
}
7678

7779
@Test
@@ -198,6 +200,27 @@ public void testDecryptDataEncryptedWithLegacyRSACipherForKeyCreatedBeforeUpgrad
198200
tryNewOrUpgradedClientAgainstNewOrOldServer(false, false, false);
199201
}
200202

203+
@Test
204+
public void testKeySupportsOAEPPadding() {
205+
final KeyStoreWrapper keyStoreWrapper = KeyStoreWrapper.getInstance();
206+
Assert.assertNotNull("KeyStoreWrapper instance should not be null", keyStoreWrapper);
207+
208+
// Create a legacy key pair without OAEP padding support
209+
keyStoreWrapper.legacyCreateKeysIfNecessary("RSA", KEY_OAEP_TEST, RSA_LENGTH);
210+
211+
// Verify the legacy key does NOT support OAEP padding
212+
Assert.assertFalse("Legacy key should not support OAEP padding",
213+
keyStoreWrapper.keySupportsOAEPPadding(KEY_OAEP_TEST));
214+
215+
// Delete the legacy key and create a modern key pair
216+
keyStoreWrapper.deleteKey(KEY_OAEP_TEST);
217+
keyStoreWrapper.getRSAPublicKey(KEY_OAEP_TEST, RSA_LENGTH); // This creates the key with modern spec
218+
219+
// Verify the modern key DOES support OAEP padding
220+
Assert.assertTrue("Modern key should support OAEP padding",
221+
keyStoreWrapper.keySupportsOAEPPadding(KEY_OAEP_TEST));
222+
}
223+
201224
/**
202225
* Helper method for tests for RSA cipher mode change
203226
* @param newClient true means new client (key generated with new code), false means upgraded client (key generated the old way)

0 commit comments

Comments
 (0)