Skip to content

Implement MSI v2 (mTLS PoP with KeyGuard Attestation) for Java Managed Identity#1014

Draft
Copilot wants to merge 2 commits intodevfrom
copilot/implement-msi-v2-mtls-pop
Draft

Implement MSI v2 (mTLS PoP with KeyGuard Attestation) for Java Managed Identity#1014
Copilot wants to merge 2 commits intodevfrom
copilot/implement-msi-v2-mtls-pop

Conversation

Copy link
Contributor

Copilot AI commented Mar 8, 2026

Adds the MSI v2 token acquisition flow for Azure Managed Identity on Java: mTLS Proof-of-Possession tokens bound to VBS-isolated KeyGuard RSA keys, attested via Windows AttestationClientLib. Feature is opt-in only and Windows-specific.

Gating logic

Both flags must be set; attestation alone is rejected:

// Triggers MSI v2 flow
ManagedIdentityParameters params = ManagedIdentityParameters.builder(resource)
    .mtlsProofOfPossession(true)
    .withAttestationSupport(true)
    .build();

// Throws MsalClientException(MSI_V2_ATTESTATION_REQUIRES_POP)
ManagedIdentityParameters.builder(resource)
    .withAttestationSupport(true)   // mtlsProofOfPossession defaults false
    .build();

MSI v2 failures always throw MsiV2Exception — no silent fallback to MSI v1.

New parameters (ManagedIdentityParameters)

  • mtlsProofOfPossession(boolean) — requests mtls_pop token type
  • withAttestationSupport(boolean) — enables KeyGuard attestation (requires PoP)

New classes

  • MsiV2 — orchestrates the 7-step flow: IMDS platform metadata → KeyGuard RSA key (JNI) → PKCS#10 CSR → attestation JWT (JNI) → IMDS /issuecredential → X.509 cert parse → mTLS token acquisition
  • MsiV2Exception — non-recoverable MSI v2 failure; never triggers fallback
  • CsrGenerator — manual DER encoding of PKCS#10 with RSA-PSS/SHA-256 and Microsoft cuId OID 1.3.6.1.4.1.311.90.2.10
  • WindowsKeyGuardJNI — JNI bridge to MsalJNIBridge.dll for NCrypt KeyGuard key ops, signing, and native mTLS
  • CsrMetadata, IssueCertificateRequest, IssueCertificateResponse — DTOs for the IMDS metadata and credential issuance endpoints

Native stubs (src/main/cpp/)

MsalJNIBridge.h/.cpp define the JNI contract and NCrypt/WinHTTP implementation pattern. Full compilation requires MSVC + Windows SDK + AttestationClientLib.dll; stub methods throw MsiV2Exception until the native DLL is provided.

Routing change (AcquireTokenByManagedIdentitySupplier)

  • Validates flag combination before any I/O
  • Dispatches to fetchNewTokenMsiV2() when both flags set — bypasses token cache (tokens are short-lived and hardware-bound)
  • MSI v1 path unchanged

New error codes

MSI_V2_ATTESTATION_REQUIRES_POP, MSI_V2_KEYGUARD_UNAVAILABLE, MSI_V2_ERROR

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • build.shibboleth.net
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/microsoft-authentication-library-for-java/microsoft-authentication-library-for-java org.codehaus.plexus.classworlds.launcher.Launcher -f pom.xml -B -V -e -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Dspotbugs.skip -Denforcer.skip -Dmaven.javadoc.skip (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

Implement MSI v2 (mTLS PoP with KeyGuard Attestation) for Java Managed Identity

Overview

Implement MSI v2 token acquisition flow for Azure Managed Identity on Java, adding support for mTLS Proof-of-Possession (PoP) tokens bound to KeyGuard-protected certificates. This feature enables secure, short-lived token acquisition with hardware-backed key protection on Windows.

Architecture

Key Design Pattern (from Python PR #882)

  • Gating: MSI v2 is opt-in only when BOTH mtlsProofOfPossession=true AND withAttestationSupport=true
  • Validation: with_attestation_support requires mtls_proof_of_possession (error if attestation alone)
  • No Silent Fallback: If MSI v2 explicitly requested and fails, raise MsiV2Error (never fall back to v1)
  • KeyGuard Mandatory: Attestation requires KeyGuard-backed RSA key (if unavailable, fail loudly)

7-Step MSI v2 Flow

  1. Get IMDS Metadata: Call /metadata/identity/getPlatformMetadata → get clientId, tenantId, cuId, attestationEndpoint
  2. Create KeyGuard Key: Via JNI → C++ → NCrypt.dll → VBS-isolated RSA-2048 key (non-exportable, per-boot)
  3. Build PKCS#10 CSR: Sign with KeyGuard key, add OID 1.3.6.1.4.1.311.90.2.10 (cuId as UTF8String(JSON))
  4. Get Attestation JWT: Via JNI → C++ → AttestationClientLib.dll → MAA attestation service
  5. Issue Credential: POST to /metadata/identity/issuecredential with CSR + attestation JWT
  6. Parse Certificate: Extract X.509 certificate from IMDS response
  7. Acquire mTLS Token: Open TLS connection to regional ESTS endpoint with client certificate, request token_type=mtls_pop

Files to Create/Modify

Modify Existing

  • msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/ManagedIdentityClient.java
    • Add mtlsProofOfPossession and withAttestationSupport parameters to acquireTokenForClient()
    • Implement gating logic: requires both flags for MSI v2
    • Validation: attestation requires PoP
    • Route to MsiV2.obtainToken() when both flags true

New Files - Core Java Implementation

  • msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/MsiV2Exception.java

    • Custom exception for MSI v2 failures (no silent fallback)
  • msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/MsiV2.java

    • Main orchestration class
    • 7-step flow implementation
    • Pure Java: CSR generation, IMDS calls, certificate parsing, mTLS token acquisition
  • msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/CsrMetadata.java

    • DTO for IMDS /getPlatformMetadata response
    • Fields: clientId, tenantId, cuId (vmId/vmssId), attestationEndpoint
  • msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/CsrGenerator.java

    • Generate PKCS#10 CSR signed with RSA-PSS/SHA256
    • Add OID attribute 1.3.6.1.4.1.311.90.2.10 with cuId as UTF8String(JSON)
    • Return PEM-formatted CSR
  • msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/IssueCertificateRequest.java

    • Request DTO for IMDS /issuecredential
    • Fields: csr (base64), attestation_token (JWT)
  • msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/IssueCertificateResponse.java

    • Response DTO from IMDS /issuecredential
    • Fields: certificate (base64 DER), mtls_authentication_endpoint, tenant_id, client_id

New Files - JNI Wrapper (Windows KeyGuard + Attestation)

  • msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/WindowsKeyGuardJNI.java

    • JNI bridge to C++ for KeyGuard key creation
    • Native methods:
      • createKeyGuardRsaKeyNative(String keyName, int keySizeBits) → returns native key handle
      • getAttestationTokenNative(String endpoint, byte[] keyHandle) → returns JWT from AttestationClientLib.dll
    • Throws PlatformNotSupportedException if VBS unavailable
  • msal4j-sdk/src/main/cpp/MsalJNIBridge.cpp (C++ implementation)

    • NCryptCreatePersistedKey() with NCryptUseVirtualIsolationFlag | NCryptUsePerBootKeyFlag
    • Call AttestationClientLib.dll to obtain attestation JWT
    • Set key properties: non-exportable, 2048-bit RSA
  • msal4j-sdk/src/main/cpp/MsalJNIBridge.h (C++ header)

    • Function declarations and constants

New Files - Build & Native Library Packaging

  • pom.xml (modifications)

    • Add JNI compiler plugin (maven-nar-plugin) for Windows C++ compilation
    • Add native library packaging (MsalJNIBridge.dll)
  • README.md (update)

    • Document MSI v2 usage
    • Document Windows prerequisites (VBS enabled for KeyGuard)
    • Document native library distribution

Implementation Details

Gating Logic (ManagedIdentityClient)

public TokenResult acquireTokenForClient(
    String resource,
    boolean mtlsProofOfPossession,
    boolean withAttestationSupport
) throws Exception {
    // Validation: attestation requires PoP
    if (withAttestationSupport && !mtlsProofOfPossession) {
        throw new ManagedIdentityException(
            "with_attestation_support=True requires mtls_proof_of_possession=True"
        );
    }
    
    // MSI v2 only when BOTH flags true...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for you](https://github.com/AzureAD/microsoft-authentication-library-for-java/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.

…Identity

Co-authored-by: gladjohn <90415114+gladjohn@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement MSI v2 for Java with mTLS PoP and KeyGuard Implement MSI v2 (mTLS PoP with KeyGuard Attestation) for Java Managed Identity Mar 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants