Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e032ae7d authored by Paul Hu's avatar Paul Hu
Browse files

Fix: IKEv2 RSA VPN crash on null Keystore private key encoding

IkeV2VpnRunner crashed when setting up an IKEv2/IPSec RSA type VPN
connection. The crash occurred because the RsaPrivateKey encoding was
null when transferring Ikev2VpnProfile to VpnProfile, specifically
when handling KEYSTORE_ALIAS for the private key. This resulted in a
NullPointerException in encodeForIpsecSecret because the 'secret' was
missing. This change corrects the serialization logic in
Ikev2VpnProfile to properly handle and transfer Keystore-backed
private key aliases, preventing the secret from becoming null during
the profile conversion.

Bug: 429063885
Test: Manually verify that an IPSec RSA type VPN can be set up
      successfully.
Flag: EXEMPT bug fix
Change-Id: Ia7aaf017b28bdaa27639db826ab9dc30a46833db
parent 16157f75
Loading
Loading
Loading
Loading
+18 −2
Original line number Diff line number Diff line
@@ -532,8 +532,13 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
                break;
            case TYPE_IKEV2_IPSEC_RSA:
                profile.ipsecUserCert = certificateToPemString(mUserCert);
                final byte[] secret = mRsaPrivateKey.getEncoded();
                if (secret == null) {
                    profile.ipsecSecret =
                        PREFIX_INLINE + encodeForIpsecSecret(mRsaPrivateKey.getEncoded());
                            PREFIX_KEYSTORE_ALIAS + getAliasFromAndroidKeystore(mUserCert);
                } else {
                    profile.ipsecSecret = PREFIX_INLINE + encodeForIpsecSecret(secret);
                }
                profile.ipsecCaCert =
                        mServerRootCaCert == null ? "" : certificateToPemString(mServerRootCaCert);
                break;
@@ -559,6 +564,17 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
        }
    }

    private static String getAliasFromAndroidKeystore(X509Certificate cert) {
        try {
            final KeyStore keystore = KeyStore.getInstance(ANDROID_KEYSTORE_PROVIDER);
            keystore.load(null);
            return checkNotNull(
                    keystore.getCertificateAlias(cert), MISSING_PARAM_MSG_TMPL, "secret");
        } catch (Exception e) {
            throw new IllegalStateException("Failed to load key from android keystore.", e);
        }
    }

    /**
     * Builds the Ikev2VpnProfile from the given profile.
     *
+65 −0
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import static android.net.cts.util.IkeSessionTestUtils.CHILD_PARAMS;
import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS_V6;
import static android.net.cts.util.IkeSessionTestUtils.getTestIkeSessionParams;

import static com.android.internal.net.VpnProfile.TYPE_IKEV2_IPSEC_RSA;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -29,6 +31,8 @@ import static org.junit.Assert.fail;

import android.net.ipsec.ike.IkeKeyIdIdentification;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -44,6 +48,7 @@ import org.junit.runner.RunWith;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
@@ -346,6 +351,66 @@ public class Ikev2VpnProfileTest {
        assertEquals("", profile.password);
    }

    @Test
    public void testRsaWithKeystoreAliasToVpnProfileAndFromVpnProfile() throws Exception {
        final String alias = "test-rsa-keystore-alias";
        final KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
        keyStore.load(null);
        // Clean up before test, in case of previous failures
        if (keyStore.containsAlias(alias)) {
            keyStore.deleteEntry(alias);
        }

        try {
            // Generate and store a key pair in AndroidKeyStore
            final KeyPairGenerator keyPairGenerator =
                    KeyPairGenerator.getInstance(
                            KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
            keyPairGenerator.initialize(
                    new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_SIGN)
                            .setDigests(KeyProperties.DIGEST_SHA256)
                            .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
                            .build());
            final KeyPair keyPair = keyPairGenerator.generateKeyPair();
            final PrivateKey privateKey = keyPair.getPrivate();

            // The certificate is self-signed and generated automatically.
            final X509Certificate userCert = (X509Certificate) keyStore.getCertificate(alias);
            assertNotNull(userCert);

            // Create a VpnProfile and convert it to an Ikev2VpnProfile via fromVpnProfile().
            // Note: The key will be dropped in toVpnProfile() because it's unused in
            // Ikev2VpnProfile. Therefore, initialize the key to an empty string in VpnProfile to
            // ensure it won't be lost when converting Ikev2VpnProfile back to VpnProfile.
            final VpnProfile vpnProfile = new VpnProfile("");
            final String ipsecCaCert = Ikev2VpnProfile.certificateToPemString(mServerRootCa);
            final String ipsecUserCert = Ikev2VpnProfile.certificateToPemString(userCert);
            vpnProfile.server = SERVER_ADDR_STRING;
            vpnProfile.ipsecIdentifier = IDENTITY_STRING;
            vpnProfile.type = TYPE_IKEV2_IPSEC_RSA;
            vpnProfile.setAllowedAlgorithms(Ikev2VpnProfile.DEFAULT_ALGORITHMS);
            vpnProfile.ipsecSecret = Ikev2VpnProfile.PREFIX_KEYSTORE_ALIAS + alias;
            vpnProfile.ipsecCaCert = ipsecCaCert;
            vpnProfile.ipsecUserCert = ipsecUserCert;
            vpnProfile.areAuthParamsInline = true;
            final Ikev2VpnProfile profile = Ikev2VpnProfile.fromVpnProfile(vpnProfile);
            // Verify that the private key and cert are from keystore
            assertEquals(privateKey, profile.getRsaPrivateKey());
            assertNull(profile.getRsaPrivateKey().getEncoded());
            assertEquals(userCert, profile.getUserCert());
            assertEquals(mServerRootCa, profile.getServerRootCaCert());

            // Now, invoke toVpnProfile() and verify that all parameters within the VpnProfile are
            // identical.
            assertTrue(vpnProfile.equals(profile.toVpnProfile()));
        } finally {
            // Cleanup
            if (keyStore.containsAlias(alias)) {
                keyStore.deleteEntry(alias);
            }
        }
    }

    @Test
    public void testPskFromVpnProfileDiscardsIrrelevantValues() throws Exception {
        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();