C# 如何识别重复的应用程序清单证书

C# 如何识别重复的应用程序清单证书,c#,powershell,azure,azure-active-directory,C#,Powershell,Azure,Azure Active Directory,我成功地: 创建了Azure应用程序服务 在AAD注册 使用新的AzureADApplicationKeyCredential将证书添加到应用程序清单 customKeyIdentifier设置如下: [Convert]:ToBase64String($global:CertificateInfo.Certificate.GetCertHash()) 通过AcquireTokenAsync使用证书访问应用程序服务以获取访问令牌 在尝试编写Powershell以简化向应用程序添加证书的过程中,我注

我成功地:

  • 创建了Azure应用程序服务
  • 在AAD注册
  • 使用新的AzureADApplicationKeyCredential将证书添加到应用程序清单
  • customKeyIdentifier设置如下:
    [Convert]:ToBase64String($global:CertificateInfo.Certificate.GetCertHash())
  • 通过
    AcquireTokenAsync
    使用证书访问应用程序服务以获取访问令牌
  • 在尝试编写Powershell以简化向应用程序添加证书的过程中,我注意到您可以添加数量不限的重复证书。我的意思是,它们都有一个唯一的keyId,但是相同
    customKeyIdentifier
    。因此,我编写了以下代码以消除创建重复项

    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 + "'" ;
        }
    }
    
    代码不起作用,因为保存在应用程序清单中的
    customKeyIdentifier
    在保存时被Azure以某种方式修改,因此我的复制检查失败。有人知道Azure是如何修改
    customKeyIdentifier
    的,以便我可以使用我的重复支票吗

    下面是可以显示在应用程序清单中的副本的副本

      "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
    }
    
    以便人们更好地了解问题在于使用新的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"
    
    下面显示的是我的应用程序清单中的条目,第一个是我手动添加的,第二个是我使用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
    },
    
    最后,从应用程序注册中的“按键”菜单中截取一个屏幕

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

    有人知道Azure是如何修改customKeyIdentifier的吗 我能让我的支票副本生效吗

    首先,你是对的。Azure允许您多次上载一个证书。但每个上载操作都将获得证书的唯一密钥ID

    我理解你想要实现的目标。但我的脚本用于查找证书是否已上载。如果你想对上传的证书进行重复检查,我认为最好去Azure portal快速找到相同的证书,而不是使用Powershell。以下是我的脚本:

        $certs = Get-AzureADApplicationKeyCredential -ObjectId 25f83866-561f-4cf2-b7a6-d623d55864df
    
        $base64Thumbprint = [System.Convert]::ToBase64String($cer.GetCertHash()) # $cer is your local certificate
    
    
        Foreach ($certificate in $certs)
    {
            $customkeyIdentifier = $certificate.CustomKeyIdentifier
    
            $UploadedThumbprint = [System.Convert]::ToBase64String($customkeyidentifier)
            If($UploadedThumbprint -eq $base64Thumbprint)
            {
                Write-Host "This certificate is same as yours and its KeyId is" : $certificate.keyId -ForegroundColor Red
            }
    
            else
            {
                Write-Host "This certificate is different from your cert and its KeyId is" : $certificate.keyId -ForegroundColor Cyan
    
            }
    }
    
    以下是我的结果:


    希望这有帮助

    我找到了答案。我使用ILSPY检查源代码,cmdlet使用

    keyCredential.CustomKeyIdentifier = Encoding.ASCII.GetBytes(this.CustomKeyIdentifier);
    
    当生成的证书工作时,keyCertificateIdentifier显然编码和显示不正确。顺便说一句,我花了3天的时间在网上搜索并使用了来自不同地方的代码,我抄袭的每个人都有错误

    简单的解决方案是调用cmdlet Set-AzureADApplication,而不是cmdlet New-AzureADApplication-KeyCredential。这需要我在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  
    }
    
    这将产生一个清单,看起来像

      "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门户中的密钥显示如下所示

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

    因此,最后:

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

  • 下面是一个示例,说明为什么我希望对我的证书进行重复检查。我假设您通过编辑或上载新清单手动输入证书。如果我这样做,那么customKeyIdentifier就不会更改,我的重复检查逻辑也会工作。但是,如果使用New-AzureADApplicationKeyCredential cmdlet添加证书,则会在过程中修改customKeyIdentifier。