c# - 如何识别重复的应用程序 list 证书

标签 c# powershell azure azure-active-directory

我已经成功了:

  1. 创建了 Azure 应用服务
  2. 已向 AAD 注册
  3. 使用 New-AzureADApplicationKeyCredential 将证书添加到应用程序 list 中。
  4. customKeyIdentifier 设置如下: [Convert]::ToBase64String($global:CertificateInfo.Certificate.GetCertHash()) ;
  5. 使用证书访问应用服务,以通过 AcquireTokenAsync 获取访问 token 。

在尝试编写 Powershell 以便更轻松地向应用程序添加证书时,我注意到您可以添加无限数量的重复证书。我的意思是它们都有一个唯一的 keyId 但相同 自定义 key 标识符。所以我编写了以下代码来消除创建重复项

if ($global:CertificateInfo.Certificate -eq $null)
{
    throw "No certificate has been selected or created yet."
}
$filter = "DisplayName eq '" + $($DisplayName) + "'" ;
$global:CertificateInfo.Application = Get-AzureADApplication -filter $filter


$certificateThumbprint = [System.Convert]::ToBase64String($global:CertificateInfo.Certificate.GetCertHash()) ;
foreach($keyCredential in $global:CertificateInfo.Application.KeyCredentials)
{
    [String]$keyCredentialThumbPrint = [System.Convert]::ToBase64String($keyCredential.CustomkeyIdentifier) ;
    if([String]::Equals($keyCredentialThumbPrint,$certificateThumbprint,[StringComparison]::CurrentCultureIgnoreCase)) {
        throw "This certificate already exists within the keyCredentials collection with KeyId" + "'" + $keyCredential.KeyId + "'" ;
    }
}

该代码不起作用,因为应用程序 list 中保存的 customKeyIdentifier 在保存时被 Azure 以某种方式修改,因此我的重复检查失败。有谁知道 Azure 如何修改 customKeyIdentifier 以便我可以让重复检查正常工作?

下面是可以显示在您的应用程序 list 中的重复项的副本

  "keyCredentials": [
{
  "customKeyIdentifier": "N0l6V0gxM3phNGxvUUk2UnZNdFE0dWV3aDFnPQ==",
  "endDate": "2019-12-18T19:22:10Z",
  "keyId": "6bef2fd1-b163-44fd-8f70-90828a6003ef",
  "startDate": "2017-12-18T23:05:28.4976081Z",
  "type": "AsymmetricX509Cert",
  "usage": "Verify",
  "value": null
},
{
  "customKeyIdentifier": "N0l6V0gxM3phNGxvUUk2UnZNdFE0dWV3aDFnPQ==",
  "endDate": "2019-12-18T19:22:10Z",
  "keyId": "d73d0903-d86f-4277-bbe9-e1cea078b400",
  "startDate": "2017-12-18T21:30:05.8419846Z",
  "type": "AsymmetricX509Cert",
  "usage": "Verify",
  "value": null
}

为了让人们更好地理解问题在于使用 New-AzureADApplicationKeyCredential cmdlet,而不是在重复的比较逻辑中,我在下面包含了我正在使用的 Powershell 代码

$global:CertificateInfo = @{} ;
function Connect-Azure {
   [CmdletBinding()]
    param
    (
            [parameter(Mandatory=$true)] 
            [string] $TenantId
    )
    Write-Host "Connect-Azure - Enter                                   - $($MyInvocation.MyCommand.Name)"
    Write-Host "Get-ApplicatonKeyCredentials - Parameters"
    Write-Host "                               TenantId                 - $($TenantId)"
    $ErrorActionPreference = 'Stop';

    Connect-AzureAD -TenantId $TenantId
    Write-Host "Connect-Azure - Exit                                    - $($MyInvocation.MyCommand.Name)"
}
function Add-AzureADApplicationKeyCredential { 
   [CmdletBinding()]
    param
    (
            [parameter(Mandatory=$true)] 
            [string] $DisplayName,
            [parameter(Mandatory=$false)] 
            [Switch] $Force
    )
    Write-Host "Add-AzureADApplicationKeyCredential - Enter                    - $($MyInvocation.MyCommand.Name)"
    Write-Host "Add-AzureADApplicationKeyCredential - Parameters"
    Write-Host "                                      DisplayName              - $($DisplayName)"
    $ErrorActionPreference = 'Stop';

    if ($global:CertificateInfo.Certificate -eq $null)
    {
        throw "No certificate has been selected or created yet."
    }
    $filter = "DisplayName eq '" + $($DisplayName) + "'" ;
    $global:CertificateInfo.Application = Get-AzureADApplication -filter $filter


    $certificateThumbprint = [System.Convert]::ToBase64String($global:CertificateInfo.Certificate.GetCertHash()) ;
    foreach($keyCredential in $global:CertificateInfo.Application.KeyCredentials)
    {
        [String]$keyCredentialThumbPrint = [System.Convert]::ToBase64String($keyCredential.CustomkeyIdentifier) ;
        if([String]::Equals($keyCredentialThumbPrint,$certificateThumbprint,[StringComparison]::CurrentCultureIgnoreCase)) {
            throw "This certificate already exists within the keyCredentials collection with KeyId" + "'" + $keyCredential.KeyId + "'" ;
        }
    }


    $CertificateInfo = @{} ;
    $CertificateInfo.CustomKeyIdentifier = [Convert]::ToBase64String($global:CertificateInfo.Certificate.GetCertHash()) ;
    $CertificateInfo.Value = [System.Convert]::ToBase64String($global:CertificateInfo.Certificate.GetRawCertData()) ;
    $CertificateInfo.EndDate = $global:CertificateInfo.Certificate.NotAfter ;
    $CertificateInfo.Type = "AsymmetricX509Cert"
    $CertificateInfo.Usage = "Verify" ;
    $CertificateInfo.ObjectId = $global:CertificateInfo.Application.ObjectId ;

    New-AzureADApplicationKeyCredential @CertificateInfo;


    Write-Host "Add-AzureADApplicationKeyCredential - Exit"     

}
function Select-Certificate {
   [CmdletBinding()]
    param
    (
            [parameter(Mandatory=$false)]
            [string] $CertStoreLocation = "Cert:\LocalMachine\My",
            [parameter(Mandatory=$true)] 
            [string] $ThumbPrint
    )

    Write-Host "Create-SelfSignedCertificate - Enter                    - $($MyInvocation.MyCommand.Name)"
    Write-Host "Get-ApplicatonKeyCredentials - Parameters"    $ErrorActionPreference = 'Stop';
    $certificateLocation = $CertStoreLocation + "\" + $ThumbPrint ;
    $global:CertificateInfo.Certificate = (Get-ChildItem –Path "$($certificateLocation)")
}
function Create-SelfSignedCertificate {
   [CmdletBinding()]
    param
    (
            [parameter(Mandatory=$false)] 
            [string] $Subject, 
            [parameter(Mandatory=$false)]
            [string] $HashAlgorithm = "SHA256",
            [parameter(Mandatory=$false)]
            [string] $CertStoreLocation = "Cert:\LocalMachine\My",
            [parameter(Mandatory=$true)]
            $NotAfter
    )


   ## see https://blogs.technet.microsoft.com/scotts-it-blog/2014/12/30/working-with-certificates-in-powershell/
    Write-Host "Create-SelfSignedCertificate - Enter                    - $($MyInvocation.MyCommand.Name)"
    Write-Host "Get-ApplicatonKeyCredentials - Parameters"
    if([string]::IsNullOrEmpty($Subject)) {
        $currentDate = (Get-Date) ;
        $Subject = [String]::Format("SelfSigned{0:yyyymmddHHMMss}",$currentDate) ;
   }
    Write-Host "                               Subject                 - $($Subject)"
    Write-Host "                               HashAlgorithm           - $($HashAlgorithm)"
    Write-Host "                               CertStoreLocation       - $($CertStoreLocation)"
    Write-Host "                               NotAfter                - $($NotAfter)"
    $ErrorActionPreference = 'Stop';

    Write-Host "Create-SelfSignedCertificate - Exit                     - $($MyInvocation.MyCommand.Name)"
    $SaveChooser = New-Object -Typename System.Windows.Forms.SaveFileDialog
    $SaveChooser.CreatePrompt  = $false ;
    $SaveChooser.Title = "Save certficate" ;
    $SaveChooser.DefaultExt = "pfx" ;
    $dialogResult = $SaveChooser.ShowDialog()  
    if($dialogResult -eq [System.Windows.Forms.DialogResult]::Cancel) {        
        return ;
    }

    $CertificatePath = $SaveChooser.Filename ;

    $certificatePassword = Read-host "Please provide a password for the exported certificate."  -AsSecureString 

    $certParameters = @{} ;
    $certParameters.CertStoreLocation = $CertStoreLocation;
    $certParameters.Subject = $Subject;
    $certParameters.KeySpec = "KeyExchange";
    $certParameters.HashAlgorithm = $HashAlgorithm;
    $certParameters.CertStoreLocation = $CertStoreLocation;
    if ($NotAfter -ne $null) {
        $certParameters.NotAfter = $NotAfter;
    }
    $global:CertificateInfo.Certificate = New-SelfSignedCertificate @certParameters ;

    $certificateLocation = $CertStoreLocation + "\" + $global:CertificateInfo.Certificate.Thumbprint ;

    Export-PfxCertificate -Cert $certificateLocation  -FilePath "$($CertificatePath)" -Password $certificatePassword  
}

这就是我调用上面代码的方式:

Connect-Azure -TenantId "your tenant ID here"
Select-Certificate -ThumbPrint "your thumbprint here"
Add-AzureADApplicationKeyCredential -DisplayName "your-displayname-here"

下面显示的是我的应用程序 list 中的条目,其中第一个条目是我手动添加的,第二个条目是我使用 New-AzureADApplicationKeyCredential cmdlet 添加的。它们是相同的证书

    {
  "customKeyIdentifier": "7IzWH13za4loQI6RvMtQ4uewh1g=",
  "endDate": "2019-12-15T16:49:37Z",
  "keyId": "fd7be8fc-e44f-4d46-a0e4-fc4ef71b0833",
  "startDate": "2017-12-18T19:12:15Z",
  "type": "AsymmetricX509Cert",
  "usage": "Verify",
  "value": null
},
{
  "customKeyIdentifier": "N0l6V0gxM3phNGxvUUk2UnZNdFE0dWV3aDFnPQ==",
  "endDate": "2019-12-18T19:22:10Z",
  "keyId": "04b0e6a9-bac5-4d3f-be5e-57ddc2976886",
  "startDate": "2017-12-19T15:47:15.9136239Z",
  "type": "AsymmetricX509Cert",
  "usage": "Verify",
  "value": null
},

最后是应用程序注册中“按键”菜单的屏幕截图

enter image description here

如您所见,保留指纹的唯一方法似乎是不使用 New-AzureADApplicationKeyCredential cmdlet

最佳答案

我找到了答案。我使用 ILSPY 检查源代码,cmdlet 获取字符串并使用

将其转换为 byte[]
keyCredential.CustomKeyIdentifier = Encoding.ASCII.GetBytes(this.CustomKeyIdentifier);

虽然生成的证书有效,但 keyCertificateIdentifier 显然编码和显示不正确。顺便说一句,我花了 3 天的时间搜索并使用了网络上各个地方的代码,但我复制的每个人仍然有错误。

简单的解决方案是调用 cmdlet Set-AzureADApplication 而不是 cmdlet New-AzureADApplicationKeyCredential。这需要我在 powershell 中创建 Microsoft.Open.AzureAD.Model.KeyCredential。请随意使用下面的代码

    $global:CertificateInfo = @{} ;
function Connect-Azure {
   [CmdletBinding()]
    param
    (
            [parameter(Mandatory=$true)] 
            [string] $TenantId
    )
    Write-Host "Connect-Azure - Enter                                   - $($MyInvocation.MyCommand.Name)"
    Write-Host "Get-ApplicatonKeyCredentials - Parameters"
    Write-Host "                               TenantId                 - $($TenantId)"
    $ErrorActionPreference = 'Stop';

    Connect-AzureAD -TenantId $TenantId
    Write-Host "Connect-Azure - Exit                                    - $($MyInvocation.MyCommand.Name)"
}
function Add-AzureADApplicationKeyCredential { 
   [CmdletBinding()]
    param
    (
            [parameter(Mandatory=$true)] 
            [string] $DisplayName,
            [parameter(Mandatory=$false)] 
            [Switch] $Force
    )
    Write-Host "Add-AzureADApplicationKeyCredential - Enter                    - $($MyInvocation.MyCommand.Name)"
    Write-Host "Add-AzureADApplicationKeyCredential - Parameters"
    Write-Host "                                      DisplayName              - $($DisplayName)"
    $ErrorActionPreference = 'Stop';

    if ($global:CertificateInfo.Certificate -eq $null)
    {
        throw "No certificate has been selected or created yet."
    }
    $filter = "DisplayName eq '" + $($DisplayName) + "'" ;
    $global:CertificateInfo.Application = Get-AzureADApplication -filter $filter


    $certificateThumbprint = [System.Convert]::ToBase64String($global:CertificateInfo.Certificate.GetCertHash()) ;
    foreach($keyCredential in $global:CertificateInfo.Application.KeyCredentials)
    {
        [String]$keyCredentialThumbPrint = [System.Convert]::ToBase64String($keyCredential.CustomkeyIdentifier) ;
        if([String]::Equals($keyCredentialThumbPrint,$certificateThumbprint,[StringComparison]::CurrentCultureIgnoreCase)) {
            throw "This certificate already exists within the keyCredentials collection with KeyId" + "'" + $keyCredential.KeyId + "'" ;
        }
    }

    $keycredential = New-Object Microsoft.Open.AzureAD.Model.KeyCredential
    $keycredential.CustomKeyIdentifier = $global:CertificateInfo.Certificate.GetCertHash() ;
    $keycredential.Value = $global:CertificateInfo.Certificate.GetRawCertData() ;
    $keycredential.EndDate = $global:CertificateInfo.Certificate.NotAfter ;
    $keycredential.StartDate = $global:CertificateInfo.Certificate.NotBefore ;
    $keycredential.Type = "AsymmetricX509Cert"
    $keycredential.Usage = "Verify" ;
    $keycredential.KeyId = [Guid]::NewGuid().ToString() ;
    $global:CertificateInfo.Application.KeyCredentials.Add($keycredential) ;

    Set-AzureADApplication -ObjectID $global:CertificateInfo.Application.ObjectId -KeyCredentials $global:CertificateInfo.Application.KeyCredentials

    Write-Host "Add-AzureADApplicationKeyCredential - Exit"     

}
function Select-Certificate {
   [CmdletBinding()]
    param
    (
            [parameter(Mandatory=$false)]
            [string] $CertStoreLocation = "Cert:\LocalMachine\My",
            [parameter(Mandatory=$true)] 
            [string] $ThumbPrint
    )

    Write-Host "Create-SelfSignedCertificate - Enter                    - $($MyInvocation.MyCommand.Name)"
    Write-Host "Get-ApplicatonKeyCredentials - Parameters"    $ErrorActionPreference = 'Stop';
    $certificateLocation = $CertStoreLocation + "\" + $ThumbPrint ;
    $global:CertificateInfo.Certificate = (Get-ChildItem –Path "$($certificateLocation)")
}
function Create-SelfSignedCertificate {
   [CmdletBinding()]
    param
    (
            [parameter(Mandatory=$false)] 
            [string] $Subject, 
            [parameter(Mandatory=$false)]
            [string] $HashAlgorithm = "SHA256",
            [parameter(Mandatory=$false)]
            [string] $CertStoreLocation = "Cert:\LocalMachine\My",
            [parameter(Mandatory=$true)]
            $NotAfter
    )


   ## see https://blogs.technet.microsoft.com/scotts-it-blog/2014/12/30/working-with-certificates-in-powershell/
    Write-Host "Create-SelfSignedCertificate - Enter                    - $($MyInvocation.MyCommand.Name)"
    Write-Host "Get-ApplicatonKeyCredentials - Parameters"
    if([string]::IsNullOrEmpty($Subject)) {
        $currentDate = (Get-Date) ;
        $Subject = [String]::Format("SelfSigned{0:yyyymmddHHMMss}",$currentDate) ;
   }
    Write-Host "                               Subject                 - $($Subject)"
    Write-Host "                               HashAlgorithm           - $($HashAlgorithm)"
    Write-Host "                               CertStoreLocation       - $($CertStoreLocation)"
    Write-Host "                               NotAfter                - $($NotAfter)"
    $ErrorActionPreference = 'Stop';

    Write-Host "Create-SelfSignedCertificate - Exit                     - $($MyInvocation.MyCommand.Name)"
    $SaveChooser = New-Object -Typename System.Windows.Forms.SaveFileDialog
    $SaveChooser.CreatePrompt  = $false ;
    $SaveChooser.Title = "Save certficate" ;
    $SaveChooser.DefaultExt = "pfx" ;
    $dialogResult = $SaveChooser.ShowDialog()  
    if($dialogResult -eq [System.Windows.Forms.DialogResult]::Cancel) {        
        return ;
    }

    $CertificatePath = $SaveChooser.Filename ;

    $certificatePassword = Read-host "Please provide a password for the exported certificate."  -AsSecureString 

    $certParameters = @{} ;
    $certParameters.CertStoreLocation = $CertStoreLocation;
    $certParameters.Subject = $Subject;
    $certParameters.KeySpec = "KeyExchange";
    $certParameters.HashAlgorithm = $HashAlgorithm;
    $certParameters.CertStoreLocation = $CertStoreLocation;
    if ($NotAfter -ne $null) {
        $certParameters.NotAfter = $NotAfter;
    }
    $global:CertificateInfo.Certificate = New-SelfSignedCertificate @certParameters ;

    $certificateLocation = $CertStoreLocation + "\" + $global:CertificateInfo.Certificate.Thumbprint ;

    Export-PfxCertificate -Cert $certificateLocation  -FilePath "$($CertificatePath)" -Password $certificatePassword  
}

这会产生如下所示的 list

  "keyCredentials": [
{
  "customKeyIdentifier": "KjS6U6xucxo5kuI1YAwykzrmBKE=",
  "endDate": "2019-12-19T19:34:29Z",
  "keyId": "de9bd300-ecdc-43d0-a5a6-e946cce10019",
  "startDate": "2017-12-19T19:24:50Z",
  "type": "AsymmetricX509Cert",
  "usage": "Verify",
  "value": null
 }
],

Azure 门户中 key 的显示如下所示

enter image description here

list “KjS6U6xucxo5kuI1YAwykzrmBKE=”中的 Base64 字符串现在可以正确显示为证书指纹“2A34BA53AC6E731A3992E235600C32933AE604A1”的十六进制表示形式。

最后:

  1. 重复检查工作正常。
  2. Azure 门户正确显示正在使用的证书的指纹。
  3. 该过程是自动化的,以减少剪切/粘贴错误的可能性。

关于c# - 如何识别重复的应用程序 list 证书,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47877525/

相关文章:

python - 有没有办法以编程方式删除 Azure 中的 SQL 数据库?

c# - 亚马逊 MWS API :Retrieve multiple Images uploaded by Seller c#

c# - 如何在 F# 或 C# 中将像素绘制到位图上而不是抗锯齿点上?

c# - 通过 ODBC 传递 null

powershell - 如何删除 SharePoint Online 中的版本历史记录文件?

asp.net - 如何在 Windows Azure 上使用多个实例的 asp.net 缓存

c# - 如果以 xamarin 形式选择它,如何清除选择器?

powershell - 动态生成命令行命令,然后使用powershell调用

powershell - 无法在 Core i7 上运行 Powershell 64

angular - ng build --prod 收到错误模块没有静态导出 true