public/Get-AxisCertificate.ps1
|
<# .SYNOPSIS Lists the certificates installed on an Axis device. .DESCRIPTION The Get-AxisCertificate function returns the certificates with private key (or, with -CA, the CA certificates) installed on an Axis device. It uses the JSON Certificate Management API (AXIS OS 11.6+, GET /config/rest/cert/v1/certificates) and falls back to the SOAP operations tds:GetCertificates / tds:GetCACertificates on older firmware. The PEM/DER returned by the device is parsed so expiry, subject, serial and thumbprint are available directly. Sources: https://developer.axis.com/vapix/device-configuration/certificate-management/ https://developer.axis.com/vapix/network-video/certificate-management-api/ .PARAMETER Device Specifies the IP address or hostname of the Axis device. .PARAMETER Alias Return only the certificate with this alias (certificate ID). .PARAMETER CA Return the CA certificates instead of the certificates with private key. .EXAMPLE Get-AxisCertificate -Device "192.168.1.100" Alias : AxisPowershell Subject : CN=axis-b8a44fb197bb.example.com Issuer : CN=Example Issuing CA NotBefore : 2026-01-01 10:00:00 NotAfter : 2027-01-01 10:00:00 Serial : 1A2B3C Thumbprint: 0F... Keystore : TPM0 KeyType : NIST-P256 Source : REST .EXAMPLE Get-AxisCertificate -Device "192.168.1.100" -CA Lists the trusted CA certificates on the device. #> function Get-AxisCertificate { [cmdletbinding()] Param( [Parameter(Mandatory)] [String]$Device, [Parameter()] [String]$Alias, [Parameter()] [Switch]$CA ) $Entries = @() $Source = "REST" #JSON API first $Path = "/certificates" if($CA) { $Path = "/ca_certificates" } Try { $Data = Invoke-AxisCertApi -Device $Device -Path $Path ForEach($Item in @($Data)) { if(!$Item) { continue } $Entries += [pscustomobject]@{ Alias = [String]$Item.alias Raw = [String]$Item.certificate Keystore = [String]$Item.keystore } } } Catch { if($_.Exception.Message -notlike "*Command not supported*") { Throw "Unable to list certificates on $($Device): $_" } $Source = "SOAP" } #SOAP fallback (firmware without the Device Configuration framework) if($Source -eq "SOAP") { if($CA) { $Body = '<tds:GetCACertificates xmlns="http://www.onvif.org/ver10/device/wsdl" />' $NodeName = "CACertificate" } else { $Body = '<tds:GetCertificates xmlns="http://www.onvif.org/ver10/device/wsdl" />' $NodeName = "NvtCertificate" } Try { $Response = Invoke-AxisSoapRequest -Device $Device -Body $Body } Catch { Throw "Unable to list certificates on $($Device): $_" } #Prefix-agnostic lookup (tds:NvtCertificate / tt:CertificateID / tt:Certificate/tt:Data) $Nodes = $Response.SelectNodes("//*[local-name()='$NodeName']") ForEach($Node in $Nodes) { $IdNode = $Node.SelectSingleNode("*[local-name()='CertificateID']") $DataNode = $Node.SelectSingleNode("*[local-name()='Certificate']/*[local-name()='Data']") $Entries += [pscustomobject]@{ Alias = [String]$IdNode.InnerText Raw = [String]$DataNode.InnerText Keystore = $null } } } if($Alias) { $Entries = @($Entries | Where-Object { $_.Alias -eq $Alias }) } ForEach($Entry in $Entries) { $Certificate = $null $Raw = $Entry.Raw.Trim() if($Raw) { Try { if($Raw -like "*-----BEGIN*") { $Certificate = ConvertFrom-PemString -Pem $Raw -AsCertificate | Select-Object -First 1 } else { #ONVIF returns base64 DER $Bytes = [Convert]::FromBase64String(($Raw -replace '\s','')) $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(,$Bytes) } } Catch { Write-Warning "Could not parse certificate '$($Entry.Alias)' on $($Device): $_" } } #Public key in Axis key_type notation (RSA-2048 / NIST-P256); extension methods work on .NET 4.6.1+ and .NET 8 $KeyType = $null if($Certificate) { $KeyType = $Certificate.PublicKey.Oid.FriendlyName Try { switch($Certificate.PublicKey.Oid.Value) { "1.2.840.113549.1.1.1" { #PublicKey.Key.KeySize is write-only on some .NET builds; read the modulus length from the encoded key $Key = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPublicKey($Certificate) $KeyType = "RSA-$($Key.ExportParameters($false).Modulus.Length * 8)" } "1.2.840.10045.2.1" { $Key = [System.Security.Cryptography.X509Certificates.ECDsaCertificateExtensions]::GetECDsaPublicKey($Certificate) $KeyType = "NIST-P$($Key.KeySize)" } } } Catch { Write-Verbose "Could not determine key size for '$($Entry.Alias)': $_" } } [pscustomobject]@{ Alias = $Entry.Alias Subject = if($Certificate) { $Certificate.Subject } else { $null } Issuer = if($Certificate) { $Certificate.Issuer } else { $null } NotBefore = if($Certificate) { $Certificate.NotBefore } else { $null } NotAfter = if($Certificate) { $Certificate.NotAfter } else { $null } Serial = if($Certificate) { $Certificate.SerialNumber.ToUpper() } else { $null } Thumbprint = if($Certificate) { $Certificate.Thumbprint } else { $null } Keystore = $Entry.Keystore KeyType = $KeyType Source = $Source Certificate = $Certificate } } } |