Support converting IkeAuthDigitalSignConfig to/from PersistableBundle
Bug: 163604823 Test: FrameworksVcnTests(add new tests) Change-Id: I62cdf4cb0297a394e0c97973e621b5c051ab0192
This commit is contained in:
@@ -18,18 +18,24 @@ package android.net.vcn.persistablebundleutils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* CertUtils provides utility methods for constructing Certificate.
|
||||
* CertUtils provides utility methods for constructing Certificate and PrivateKey.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class CertUtils {
|
||||
private static final String CERT_TYPE_X509 = "X.509";
|
||||
private static final String PRIVATE_KEY_TYPE_RSA = "RSA";
|
||||
|
||||
/** Decodes an ASN.1 DER encoded Certificate */
|
||||
public static X509Certificate certificateFromByteArray(byte[] derEncoded) {
|
||||
@@ -43,4 +49,18 @@ public class CertUtils {
|
||||
throw new IllegalArgumentException("Fail to decode certificate", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Decodes a PKCS#8 encoded RSA private key */
|
||||
public static RSAPrivateKey privateKeyFromByteArray(byte[] pkcs8Encoded) {
|
||||
Objects.requireNonNull(pkcs8Encoded, "pkcs8Encoded was null");
|
||||
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(pkcs8Encoded);
|
||||
|
||||
try {
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(PRIVATE_KEY_TYPE_RSA);
|
||||
|
||||
return (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||
throw new IllegalArgumentException("Fail to decode PrivateKey", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +22,18 @@ import android.annotation.NonNull;
|
||||
import android.net.ipsec.ike.IkeSaProposal;
|
||||
import android.net.ipsec.ike.IkeSessionParams;
|
||||
import android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig;
|
||||
import android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignLocalConfig;
|
||||
import android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignRemoteConfig;
|
||||
import android.net.ipsec.ike.IkeSessionParams.IkeAuthPskConfig;
|
||||
import android.os.PersistableBundle;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.vcn.util.PersistableBundleUtils;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@@ -146,6 +152,14 @@ public final class IkeSessionParamsUtils {
|
||||
IkeAuthPskConfig config = (IkeAuthPskConfig) authConfig;
|
||||
return IkeAuthPskConfigUtils.toPersistableBundle(
|
||||
config, createPersistableBundle(IKE_AUTH_METHOD_PSK));
|
||||
} else if (authConfig instanceof IkeAuthDigitalSignLocalConfig) {
|
||||
IkeAuthDigitalSignLocalConfig config = (IkeAuthDigitalSignLocalConfig) authConfig;
|
||||
return IkeAuthDigitalSignConfigUtils.toPersistableBundle(
|
||||
config, createPersistableBundle(IKE_AUTH_METHOD_PUB_KEY_SIGNATURE));
|
||||
} else if (authConfig instanceof IkeAuthDigitalSignRemoteConfig) {
|
||||
IkeAuthDigitalSignRemoteConfig config = (IkeAuthDigitalSignRemoteConfig) authConfig;
|
||||
return IkeAuthDigitalSignConfigUtils.toPersistableBundle(
|
||||
config, createPersistableBundle(IKE_AUTH_METHOD_PUB_KEY_SIGNATURE));
|
||||
} else {
|
||||
throw new IllegalStateException("Invalid IkeAuthConfig subclass");
|
||||
}
|
||||
@@ -178,6 +192,15 @@ public final class IkeSessionParamsUtils {
|
||||
IkeAuthPskConfigUtils.setBuilderByReadingPersistableBundle(
|
||||
localAuthBundle, remoteAuthBundle, builder);
|
||||
break;
|
||||
case IKE_AUTH_METHOD_PUB_KEY_SIGNATURE:
|
||||
if (remoteMethodType != IKE_AUTH_METHOD_PUB_KEY_SIGNATURE) {
|
||||
throw new IllegalArgumentException(
|
||||
"Expect remote auth method to be digital signature based, but was "
|
||||
+ remoteMethodType);
|
||||
}
|
||||
IkeAuthDigitalSignConfigUtils.setBuilderByReadingPersistableBundle(
|
||||
localAuthBundle, remoteAuthBundle, builder);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid EAP method type " + localMethodType);
|
||||
@@ -218,4 +241,102 @@ public final class IkeSessionParamsUtils {
|
||||
builder.setAuthPsk(localPsk);
|
||||
}
|
||||
}
|
||||
|
||||
private static class IkeAuthDigitalSignConfigUtils {
|
||||
private static final String END_CERT_KEY = "END_CERT_KEY";
|
||||
private static final String INTERMEDIATE_CERTS_KEY = "INTERMEDIATE_CERTS_KEY";
|
||||
private static final String PRIVATE_KEY_KEY = "PRIVATE_KEY_KEY";
|
||||
private static final String TRUST_CERT_KEY = "TRUST_CERT_KEY";
|
||||
|
||||
@NonNull
|
||||
public static PersistableBundle toPersistableBundle(
|
||||
@NonNull IkeAuthDigitalSignLocalConfig config, @NonNull PersistableBundle result) {
|
||||
try {
|
||||
result.putPersistableBundle(
|
||||
END_CERT_KEY,
|
||||
PersistableBundleUtils.fromByteArray(
|
||||
config.getClientEndCertificate().getEncoded()));
|
||||
|
||||
final List<X509Certificate> certList = config.getIntermediateCertificates();
|
||||
final List<byte[]> encodedCertList = new ArrayList<>(certList.size());
|
||||
for (X509Certificate cert : certList) {
|
||||
encodedCertList.add(cert.getEncoded());
|
||||
}
|
||||
|
||||
final PersistableBundle certsBundle =
|
||||
PersistableBundleUtils.fromList(
|
||||
encodedCertList, PersistableBundleUtils::fromByteArray);
|
||||
result.putPersistableBundle(INTERMEDIATE_CERTS_KEY, certsBundle);
|
||||
} catch (CertificateEncodingException e) {
|
||||
throw new IllegalArgumentException("Fail to encode certificate");
|
||||
}
|
||||
|
||||
// TODO: b/170670506 Consider putting PrivateKey in Android KeyStore
|
||||
result.putPersistableBundle(
|
||||
PRIVATE_KEY_KEY,
|
||||
PersistableBundleUtils.fromByteArray(config.getPrivateKey().getEncoded()));
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static PersistableBundle toPersistableBundle(
|
||||
@NonNull IkeAuthDigitalSignRemoteConfig config, @NonNull PersistableBundle result) {
|
||||
try {
|
||||
X509Certificate caCert = config.getRemoteCaCert();
|
||||
if (caCert != null) {
|
||||
result.putPersistableBundle(
|
||||
TRUST_CERT_KEY,
|
||||
PersistableBundleUtils.fromByteArray(caCert.getEncoded()));
|
||||
}
|
||||
} catch (CertificateEncodingException e) {
|
||||
throw new IllegalArgumentException("Fail to encode the certificate");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void setBuilderByReadingPersistableBundle(
|
||||
@NonNull PersistableBundle localAuthBundle,
|
||||
@NonNull PersistableBundle remoteAuthBundle,
|
||||
@NonNull IkeSessionParams.Builder builder) {
|
||||
Objects.requireNonNull(localAuthBundle, "localAuthBundle was null");
|
||||
Objects.requireNonNull(remoteAuthBundle, "remoteAuthBundle was null");
|
||||
|
||||
// Deserialize localAuth
|
||||
final PersistableBundle endCertBundle =
|
||||
localAuthBundle.getPersistableBundle(END_CERT_KEY);
|
||||
Objects.requireNonNull(endCertBundle, "End cert was null");
|
||||
final byte[] encodedCert = PersistableBundleUtils.toByteArray(endCertBundle);
|
||||
final X509Certificate endCert = CertUtils.certificateFromByteArray(encodedCert);
|
||||
|
||||
final PersistableBundle certsBundle =
|
||||
localAuthBundle.getPersistableBundle(INTERMEDIATE_CERTS_KEY);
|
||||
Objects.requireNonNull(certsBundle, "Intermediate certs was null");
|
||||
final List<byte[]> encodedCertList =
|
||||
PersistableBundleUtils.toList(certsBundle, PersistableBundleUtils::toByteArray);
|
||||
final List<X509Certificate> certList = new ArrayList<>(encodedCertList.size());
|
||||
for (byte[] encoded : encodedCertList) {
|
||||
certList.add(CertUtils.certificateFromByteArray(encoded));
|
||||
}
|
||||
|
||||
final PersistableBundle privateKeyBundle =
|
||||
localAuthBundle.getPersistableBundle(PRIVATE_KEY_KEY);
|
||||
Objects.requireNonNull(privateKeyBundle, "PrivateKey bundle was null");
|
||||
final PrivateKey privateKey =
|
||||
CertUtils.privateKeyFromByteArray(
|
||||
PersistableBundleUtils.toByteArray(privateKeyBundle));
|
||||
|
||||
// Deserialize remoteAuth
|
||||
final PersistableBundle trustCertBundle =
|
||||
remoteAuthBundle.getPersistableBundle(TRUST_CERT_KEY);
|
||||
|
||||
X509Certificate caCert = null;
|
||||
if (trustCertBundle != null) {
|
||||
final byte[] encodedCaCert = PersistableBundleUtils.toByteArray(trustCertBundle);
|
||||
caCert = CertUtils.certificateFromByteArray(encodedCaCert);
|
||||
}
|
||||
|
||||
builder.setAuthDigitalSignature(caCert, endCert, certList, privateKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
21
tests/vcn/assets/client-end-cert.pem
Normal file
21
tests/vcn/assets/client-end-cert.pem
Normal file
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDaDCCAlCgAwIBAgIIcorRI3n29E4wDQYJKoZIhvcNAQELBQAwQTELMAkGA1UE
|
||||
BhMCVVMxEDAOBgNVBAoTB0FuZHJvaWQxIDAeBgNVBAMTF3R3by5jYS50ZXN0LmFu
|
||||
ZHJvaWQubmV0MB4XDTIwMDQxNDA1MDM0OVoXDTIzMDQxNDA1MDM0OVowRTELMAkG
|
||||
A1UEBhMCVVMxEDAOBgNVBAoTB0FuZHJvaWQxJDAiBgNVBAMTG2NsaWVudC50ZXN0
|
||||
LmlrZS5hbmRyb2lkLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||
AK/cK+sIaiQlJYvy5+Dq70sJbgR7PO1uS2qkLRP7Wb3z5SNvz94nQvZRrFn1AFIE
|
||||
CpfESh5kUF6gJe7t7NR3mpQ98iEosCRBMDJT8qB+EeHiL4wkrmCE9sYMTyvaApRc
|
||||
6Qzozn/9kKma7Qpj/25AvoPluTERqhZ6AQ77BJeb6FNOAoO1Aoe9GJuB1xmRxjRw
|
||||
D0mwusL+ciQ/7uKlsFP5VO5XqACcohXSerzO8jcD9necBvka3SDepqqzn1K0NPRC
|
||||
25fMmS5kSjddKtKOif7w2NI3OpVsmP3kHv66If73VURsy0lgXPYyKkq8lAMrtmXG
|
||||
R7svFGPbEl+Swkpr3b+dzF8CAwEAAaNgMF4wHwYDVR0jBBgwFoAUcqSu1uRYT/DL
|
||||
bLoDNUz38nGvCKQwJgYDVR0RBB8wHYIbY2xpZW50LnRlc3QuaWtlLmFuZHJvaWQu
|
||||
bmV0MBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQCa53tK
|
||||
I9RM9/MutZ5KNG2Gfs2cqaPyv8ZRhs90HDWZhkFVu7prywJAxOd2hxxHPsvgurio
|
||||
4bKAxnT4EXevgz5YoCbj2TPIL9TdFYh59zZ97XXMxk+SRdypgF70M6ETqKPs3hDP
|
||||
ZRMMoHvvYaqaPvp4StSBX9A44gSyjHxVYJkrjDZ0uffKg5lFL5IPvqfdmSRSpGab
|
||||
SyGTP4OLTy0QiNV3pBsJGdl0h5BzuTPR9OTl4xgeqqBQy2bDjmfJBuiYyCSCkPi7
|
||||
T3ohDYCymhuSkuktHPNG1aKllUJaw0tuZuNydlgdAveXPYfM36uvK0sfd9qr9pAy
|
||||
rmkYV2MAWguFeckh
|
||||
-----END CERTIFICATE-----
|
||||
28
tests/vcn/assets/client-private-key.key
Normal file
28
tests/vcn/assets/client-private-key.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCv3CvrCGokJSWL
|
||||
8ufg6u9LCW4EezztbktqpC0T+1m98+Ujb8/eJ0L2UaxZ9QBSBAqXxEoeZFBeoCXu
|
||||
7ezUd5qUPfIhKLAkQTAyU/KgfhHh4i+MJK5ghPbGDE8r2gKUXOkM6M5//ZCpmu0K
|
||||
Y/9uQL6D5bkxEaoWegEO+wSXm+hTTgKDtQKHvRibgdcZkcY0cA9JsLrC/nIkP+7i
|
||||
pbBT+VTuV6gAnKIV0nq8zvI3A/Z3nAb5Gt0g3qaqs59StDT0QtuXzJkuZEo3XSrS
|
||||
jon+8NjSNzqVbJj95B7+uiH+91VEbMtJYFz2MipKvJQDK7Zlxke7LxRj2xJfksJK
|
||||
a92/ncxfAgMBAAECggEAQztaMvW5lm35J8LKsWs/5qEJRX9T8LWs8W0oqq36Riub
|
||||
G2wgvR6ndAIPcSjAYZqX7iOl7m6NZ0+0kN63HxdGqovwKIskpAekBGmhpYftED1n
|
||||
zh0r6UyMB3UnQ22KdOv8UOokIDxxdNX8728BdUYdT9Ggdkj5jLRB+VcwD0IUlNvo
|
||||
zzTpURV9HEd87uiLqd4AAHXSI0lIHI5U43z24HI/J6/YbYHT3Rlh6CIa/LuwO6vL
|
||||
gFkgqg0/oy6yJtjrHtzNVA67F0UaH62hR4YFgbC0d955SJnDidWOv/0j2DMpfdCc
|
||||
9kFAcPwUSyykvUSLnGIKWSG4D+6gzIeAeUx4oO7kMQKBgQDVNRkX8AGTHyLg+NXf
|
||||
spUWWcodwVioXl30Q7h6+4bt8OI61UbhQ7wX61wvJ1cySpa2KOYa2UdagQVhGhhL
|
||||
ADu363R77uXF/jZgzVfmjjyJ2nfDqRgHWRTlSkuq/jCOQCz7VIPHRZg5WL/9D4ms
|
||||
TAqMjpzqeMfFZI+w4/+xpcJIuQKBgQDTKBy+ZuerWrVT9icWKvLU58o5EVj/2yFy
|
||||
GJvKm+wRAAX2WzjNnR4HVd4DmMREVz1BPYby0j5gqjvtDsxYYu39+NT7JvMioLLK
|
||||
QPj+7k5geYgNqVgCxB1vP89RhY2X1RLrN9sTXOodgFPeXOQWNYITkGp3eQpx4nTJ
|
||||
+K/al3oB1wKBgAjnc8nVIyuyxDEjE0OJYMKTM2a0uXAmqMPXxC+Wq5bqVXhhidlE
|
||||
i+lv0eTCPtkB1nN7F8kNQ/aaps/cWCFhvBy9P5shagUvzbOTP9WIIS0cq53HRRKh
|
||||
fMbqqGhWv05hjb9dUzeSR341n6cA7B3++v3Nwu3j52vt/DZF/1q68nc5AoGAS0SU
|
||||
ImbKE/GsizZGLoe2sZ/CHN+LKwCwhlwxRGKaHmE0vuE7eUeVSaYZEo0lAPtb8WJ+
|
||||
NRYueASWgeTxgFwbW5mUScZTirdfo+rPFwhZVdhcYApKPgosN9i2DOgfVcz1BnWN
|
||||
mPRY25U/0BaqkyQVruWeneG+kGPZn5kPDktKiVcCgYEAkzwU9vCGhm7ZVALvx/zR
|
||||
wARz2zsL9ImBc0P4DK1ld8g90FEnHrEgeI9JEwz0zFHOCMLwlk7kG0Xev7vfjZ7G
|
||||
xSqtQYOH33Qp6rtBOgdt8hSyDFvakvDl6bqhAw52gelO3MTpAB1+ZsfZ5gFx13Jf
|
||||
idNFcaIrC52PtZIH7QCzdDY=
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -23,13 +23,22 @@ import android.net.ipsec.ike.IkeFqdnIdentification;
|
||||
import android.net.ipsec.ike.IkeSessionParams;
|
||||
import android.os.PersistableBundle;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.org.bouncycastle.util.io.pem.PemObject;
|
||||
import com.android.internal.org.bouncycastle.util.io.pem.PemReader;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.InetAddress;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@@ -95,4 +104,33 @@ public class IkeSessionParamsUtilsTest {
|
||||
final IkeSessionParams params = createBuilderMinimum().setAuthPsk("psk".getBytes()).build();
|
||||
verifyPersistableBundleEncodeDecodeIsLossless(params);
|
||||
}
|
||||
|
||||
private static InputStream openAssetsFile(String fileName) throws Exception {
|
||||
return InstrumentationRegistry.getContext().getResources().getAssets().open(fileName);
|
||||
}
|
||||
|
||||
private static X509Certificate createCertFromPemFile(String fileName) throws Exception {
|
||||
final CertificateFactory factory = CertificateFactory.getInstance("X.509");
|
||||
return (X509Certificate) factory.generateCertificate(openAssetsFile(fileName));
|
||||
}
|
||||
|
||||
private static RSAPrivateKey createRsaPrivateKeyFromKeyFile(String fileName) throws Exception {
|
||||
final PemObject pemObject =
|
||||
new PemReader(new InputStreamReader(openAssetsFile(fileName))).readPemObject();
|
||||
return (RSAPrivateKey) CertUtils.privateKeyFromByteArray(pemObject.getContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeRecodeParamsWithDigitalSignAuth() throws Exception {
|
||||
final X509Certificate serverCaCert = createCertFromPemFile("self-signed-ca.pem");
|
||||
final X509Certificate clientEndCert = createCertFromPemFile("client-end-cert.pem");
|
||||
final RSAPrivateKey clientPrivateKey =
|
||||
createRsaPrivateKeyFromKeyFile("client-private-key.key");
|
||||
|
||||
final IkeSessionParams params =
|
||||
createBuilderMinimum()
|
||||
.setAuthDigitalSignature(serverCaCert, clientEndCert, clientPrivateKey)
|
||||
.build();
|
||||
verifyPersistableBundleEncodeDecodeIsLossless(params);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user