Skip to content

Instantly share code, notes, and snippets.

@rbrayb
Last active January 20, 2026 22:34
Show Gist options
  • Select an option

  • Save rbrayb/18889762d062e758b600c4611c55a9d9 to your computer and use it in GitHub Desktop.

Select an option

Save rbrayb/18889762d062e758b600c4611c55a9d9 to your computer and use it in GitHub Desktop.
Azure AD B2C to Entra External ID (EEID) Migration Kit - Configure-ExternalIdJit
# FormatPemWithSlashN.ps1 - Convert PEM key to JSON-safe format
#
# This script reads a PEM-encoded private key file and converts it to a format
# that can be safely embedded in JSON configuration files (e.g., appsettings.json).
# It replaces actual newlines with the literal string "\n" so the multi-line key
# becomes a single-line string suitable for JSON values.
# Path to the PEM file containing the private key
$pemPath = ".\jit-private-key.pem" # Change this to your actual PEM file path
# Read the entire PEM file as a single string (preserves original line endings)
$pem = Get-Content $pemPath -Raw
# Convert line endings to JSON-safe escape sequences
# This replaces Windows (CRLF) and Unix (LF) line endings with the literal string "\n"
$escaped = $pem -replace "`r`n", "\n" -replace "`n", "\n"
# Copy the formatted string to clipboard for easy pasting
$escaped | Set-Clipboard
# Display success message
Write-Host "`n✓ Converted key copied to clipboard!" -ForegroundColor Green
Write-Host "`nPaste into: Migration__JitAuthentication__InlineRsaPrivateKey value" -ForegroundColor Cyan
# Output the escaped content to console for verification
Write-Host "`nFormatted key:`n" -ForegroundColor Yellow
Write-Host $escaped
Write-Host "`n"
# Test-JIT-Function.ps1 - Test JIT Authentication Function locally
# Developer Guide: Step 5: Test JIT Flow: Test with HTTP Client:
# Configuration
$ngrokUrl = "https://96dcf877ef56.ngrok-free.app" # Change to your actual ngrok domain
$endpoint = "$ngrokUrl/api/JitAuthentication"
# Test payload matching External ID Custom Authentication Extension format
$payload = @{
type = "customAuthenticationExtension"
data = @{
authenticationContext = @{
correlationId = "test-12345"
user = @{
id = "user-object-id-from-external-id"
userPrincipalName = "testuser@yourdomain.com"
}
}
passwordContext = @{
userPassword = "RealB2CPassword123!"
nonce = "1234567890"
}
}
}
Write-Host ""
Write-Host "═══════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " Testing JIT Authentication Function" -ForegroundColor Cyan
Write-Host "═══════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host ""
Write-Host "Endpoint: " -NoNewline
Write-Host $endpoint -ForegroundColor Yellow
Write-Host "User: " -NoNewline
Write-Host $payload.data.authenticationContext.user.userPrincipalName -ForegroundColor Yellow
Write-Host ""
try {
# Send POST request
$response = Invoke-RestMethod -Uri $endpoint `
-Method Post `
-Body ($payload | ConvertTo-Json -Depth 10) `
-ContentType "application/json" `
-TimeoutSec 30
Write-Host "═══════════════════════════════════════════════" -ForegroundColor Green
Write-Host " ✓ SUCCESS - Response Received" -ForegroundColor Green
Write-Host "═══════════════════════════════════════════════" -ForegroundColor Green
Write-Host ""
Write-Host "Response:" -ForegroundColor Cyan
$response | ConvertTo-Json -Depth 10 | Write-Host -ForegroundColor White
Write-Host ""
}
catch {
Write-Host "═══════════════════════════════════════════════" -ForegroundColor Red
Write-Host " ✗ ERROR - Request Failed" -ForegroundColor Red
Write-Host "═══════════════════════════════════════════════" -ForegroundColor Red
Write-Host ""
Write-Host "Error Message:" -ForegroundColor Yellow
Write-Host $_.Exception.Message -ForegroundColor Red
Write-Host ""
if ($_.Exception.Response) {
Write-Host "Status Code:" -ForegroundColor Yellow
Write-Host $_.Exception.Response.StatusCode -ForegroundColor Red
}
}
# Test-JitRsaEncryption.ps1
<#
.SYNOPSIS
Tests RSA encryption/decryption using JIT certificate and private key.
.DESCRIPTION
Encrypts a random string with the public key (from certificate)
and decrypts it with the private key to verify the key pair works.
.PARAMETER CertificatePath
Path to jit-certificate.txt file (public key)
.PARAMETER PrivateKeyPath
Path to jit-private-key.pem file (private key)
.PARAMETER TestString
String to encrypt/decrypt. If not provided, generates a random string.
.EXAMPLE
.\Test-JitRsaEncryption.ps1
.EXAMPLE
.\Test-JitRsaEncryption.ps1 -TestString "MySecretPassword123!"
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]$CertificatePath = ".\jit-certificate.txt",
[Parameter(Mandatory = $false)]
[string]$PrivateKeyPath = ".\jit-private-key.pem",
[Parameter(Mandatory = $false)]
[string]$TestString
)
$ErrorActionPreference = "Stop"
Write-Host ""
Write-Host "═══════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " RSA Encryption/Decryption Test" -ForegroundColor Cyan
Write-Host "═══════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host ""
# Generate random test string if not provided
if (-not $TestString) {
$TestString = "TestPassword_" + [Guid]::NewGuid().ToString().Substring(0, 8)
Write-Host "Generated random test string" -ForegroundColor Gray
}
Write-Host "Test String: " -NoNewline -ForegroundColor Cyan
Write-Host $TestString -ForegroundColor Yellow
Write-Host ""
# ============================================================================
# Load Certificate (Public Key)
# ============================================================================
Write-Host "Loading public key from certificate..." -ForegroundColor Cyan
if (-not (Test-Path $CertificatePath)) {
Write-Host "✗ Certificate file not found: $CertificatePath" -ForegroundColor Red
exit 1
}
try {
# Read certificate base64
$certBase64 = Get-Content $CertificatePath -Raw
$certBytes = [Convert]::FromBase64String($certBase64)
# Load certificate
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($certBytes)
Write-Host "✓ Certificate loaded" -ForegroundColor Green
Write-Host " Subject: $($cert.Subject)" -ForegroundColor Gray
Write-Host " Valid: $($cert.NotBefore.ToString('yyyy-MM-dd')) to $($cert.NotAfter.ToString('yyyy-MM-dd'))" -ForegroundColor Gray
# Get RSA public key from certificate
$rsaPublic = $cert.PublicKey.Key
}
catch {
Write-Host "✗ Failed to load certificate: $_" -ForegroundColor Red
exit 1
}
# ============================================================================
# Load Private Key
# ============================================================================
Write-Host ""
Write-Host "Loading private key from PEM file..." -ForegroundColor Cyan
if (-not (Test-Path $PrivateKeyPath)) {
Write-Host "✗ Private key file not found: $PrivateKeyPath" -ForegroundColor Red
exit 1
}
try {
# Read PEM file
$pemContent = Get-Content $PrivateKeyPath -Raw
# Remove PEM headers/footers and whitespace
$pemContent = $pemContent -replace "-----BEGIN PRIVATE KEY-----", ""
$pemContent = $pemContent -replace "-----END PRIVATE KEY-----", ""
$pemContent = $pemContent -replace "\s+", ""
# Decode base64
$privateKeyBytes = [Convert]::FromBase64String($pemContent)
# Import PKCS#8 private key
$rsaPrivate = [System.Security.Cryptography.RSA]::Create()
$rsaPrivate.ImportPkcs8PrivateKey($privateKeyBytes, [ref]$null)
Write-Host "✓ Private key loaded" -ForegroundColor Green
Write-Host " Key Size: $($rsaPrivate.KeySize) bits" -ForegroundColor Gray
}
catch {
Write-Host "✗ Failed to load private key: $_" -ForegroundColor Red
exit 1
}
# ============================================================================
# Encrypt with Public Key
# ============================================================================
Write-Host ""
Write-Host "Encrypting with public key..." -ForegroundColor Cyan
try {
# Convert string to bytes
$plainBytes = [System.Text.Encoding]::UTF8.GetBytes($TestString)
# Encrypt using RSA-OAEP with SHA-256
$encryptedBytes = $rsaPublic.Encrypt($plainBytes, [System.Security.Cryptography.RSAEncryptionPadding]::OaepSHA256)
# Convert to base64 for display
$encryptedBase64 = [Convert]::ToBase64String($encryptedBytes)
Write-Host "✓ Encryption successful" -ForegroundColor Green
Write-Host " Encrypted (base64): $($encryptedBase64.Substring(0, [Math]::Min(60, $encryptedBase64.Length)))..." -ForegroundColor Gray
Write-Host " Size: $($encryptedBytes.Length) bytes" -ForegroundColor Gray
}
catch {
Write-Host "✗ Encryption failed: $_" -ForegroundColor Red
exit 1
}
# ============================================================================
# Decrypt with Private Key
# ============================================================================
Write-Host ""
Write-Host "Decrypting with private key..." -ForegroundColor Cyan
try {
# Decrypt using RSA-OAEP with SHA-256
$decryptedBytes = $rsaPrivate.Decrypt($encryptedBytes, [System.Security.Cryptography.RSAEncryptionPadding]::OaepSHA256)
# Convert bytes back to string
$decryptedString = [System.Text.Encoding]::UTF8.GetString($decryptedBytes)
Write-Host "✓ Decryption successful" -ForegroundColor Green
Write-Host " Decrypted: " -NoNewline -ForegroundColor Gray
Write-Host $decryptedString -ForegroundColor Yellow
}
catch {
Write-Host "✗ Decryption failed: $_" -ForegroundColor Red
exit 1
}
# ============================================================================
# Verify
# ============================================================================
Write-Host ""
Write-Host "Verifying..." -ForegroundColor Cyan
if ($TestString -eq $decryptedString) {
Write-Host "✓ SUCCESS! Original and decrypted strings match!" -ForegroundColor Green
Write-Host ""
Write-Host " Original: $TestString" -ForegroundColor White
Write-Host " Decrypted: $decryptedString" -ForegroundColor White
Write-Host ""
Write-Host "✓ RSA key pair is working correctly!" -ForegroundColor Green
} else {
Write-Host "✗ FAILURE! Strings do not match!" -ForegroundColor Red
Write-Host " Original: $TestString" -ForegroundColor White
Write-Host " Decrypted: $decryptedString" -ForegroundColor White
exit 1
}
Write-Host ""
Write-Host "═══════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " Test Summary" -ForegroundColor Cyan
Write-Host "═══════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host ""
Write-Host "Certificate: $CertificatePath" -ForegroundColor Gray
Write-Host "Private Key: $PrivateKeyPath" -ForegroundColor Gray
Write-Host "Key Size: $($rsaPrivate.KeySize) bits" -ForegroundColor Gray
Write-Host "Padding: OAEP-SHA256" -ForegroundColor Gray
Write-Host "Test String: $TestString" -ForegroundColor Gray
Write-Host "Status: " -NoNewline -ForegroundColor Gray
Write-Host "PASSED ✓" -ForegroundColor Green
Write-Host ""
# Cleanup
$rsaPublic.Dispose()
$rsaPrivate.Dispose()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment