I am developing an Android EST (Enrollment over Secure Transport) client to securely generate and enroll X.509 certificates using mutual TLS (mTLS). (We need to enroll device with EST protocol instead of SCEP which is less secure, and I didn't found any MDM solution supporting EST)
this is the workflow
1 Generate a key pair on the Android device (ECDSA stored in Android Keystore).
2 Create a Certificate Signing Request (CSR) using BouncyCastle.signal
3 Send the CSR to the Mobile Device Management (MDM) server.
4 The MDM forwards the CSR to the EST server, which submits it to the PKI for signing.
5 Once signed, the PKI returns the certificate to the EST server, which sends it back to the MDM.
6 The MDM pushes the full VPN configuration (including the signed certificate) to the Android device.
If anyone has implemented CSR generation with BouncyCastle or any other opensource technology on Android recently, your insights would be greatly appreciated!
Thanks in advance for your guidance!
I’m struggling to generate a valid code to generate CSR request, everything I've found about BouncyCastle seem outdated..
package com.example.keygenerationapp;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.security.KeyChain;
import android.security.KeyChainException;
import .bouncycastle.asn1.x500.X500Name;
import .bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import .bouncycastle.pkcs.PKCS10CertificationRequest;
import .bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import .bouncycastle.operator.ContentSigner;
import .bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import .bouncycastle.openssl.jcajce.JcaPEMWriter;
import java.io.FileWriter;
import java.security.*;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.List;
import javax.security.auth.x500.X500Principal;
import java.io.IOException;
import java.util.Objects;
public class KeyGenerationAndCSR {
private static String devicePolicyService;
private DevicePolicyManager devicePolicyManager;
private ComponentName componentName;
public KeyGenerationAndCSR(DevicePolicyManager dpm, ComponentName cn) {
devicePolicyManager = dpm;
componentName = cn;
}
// Generate an RSA key pair and a certificate
public KeyPair generateKeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048, new SecureRandom()); // 2048 bits for the RSA key
return keyPairGenerator.generateKeyPair();
}
// Method to retrieve the certificate chain associated with the private key alias
public List<Certificate> getCertificateChain(String alias) throws KeyChainException, InterruptedException {
// Using KeyChain to get the certificate chain
return Arrays.asList(Objects.requireNonNull(KeyChain.getCertificateChain(null, alias)));
}
// Create a CSR (Certificate Signing Request) using Bouncy Castle
public PKCS10CertificationRequest generateCSR(KeyPair keyPair) throws Exception {
// Create the certificate subject (e.g., CN=MyDevice)
X500Principal subjectPrincipal = new X500Principal("CN=MyDevice");
// Convert X500Principal to X500Name
X500Name subject = new X500Name(subjectPrincipal.getName());
// Convert the public key to SubjectPublicKeyInfo
PublicKey publicKey = keyPair.getPublic();
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded());
// Use the private key to sign the CSR
PKCS10CertificationRequestBuilder p10Builder = new PKCS10CertificationRequestBuilder(subject, publicKeyInfo);
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate());
PKCS10CertificationRequest csr = p10Builder.build(signer);
// Return the generated CSR
return csr;
}
// Install the certificate associated with the private key in the keystore
public boolean setCertificateForKey(String alias, List<Certificate> certificateChain) {
return devicePolicyManager.setKeyPairCertificate(
componentName,
alias,
certificateChain,
false // Not selectable by the user
);
}
// Save the CSR as a PEM file
public void saveCSRToFile(PKCS10CertificationRequest csr) throws IOException {
try (JcaPEMWriter pemWriter = new JcaPEMWriter(new FileWriter("csr.pem"))) {
pemWriter.writeObject(csr);
}
}
public static void main(String[] args) throws Exception {
// Simulate the initialization of the DevicePolicyManager and ComponentName objects
DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName componentName = new ComponentName("com.example.app", "com.example.app.DeviceAdminReceiver");
// Create the KeyGenerationAndCSR object
KeyGenerationAndCSR generator = new KeyGenerationAndCSR(dpm, componentName);
// Generate the key and the CSR
KeyPair keyPair = generator.generateKeyPair();
PKCS10CertificationRequest csr = generator.generateCSR(keyPair);
// Save the CSR as a PEM file
generator.saveCSRToFile(csr);
}
private static Object getSystemService(String devicePolicyService) {
KeyGenerationAndCSR.devicePolicyService = devicePolicyService;
return null;
}
}