续Windows 2008 R2之三十五ADCS实现跨森林注册(一)

5、复制”用户“证书模板为”跨森林用户证书“,相关设设置如下图

打开证书颁发机构,右击”证书模板“选择”管理“

Windows 2008 R2之三十六ADCS实现跨森林注册(二)_第1张图片

右击”用户“证书,选择”复制“

Windows 2008 R2之三十六ADCS实现跨森林注册(二)_第2张图片

Windows 2008 R2之三十六ADCS实现跨森林注册(二)_第3张图片

Windows 2008 R2之三十六ADCS实现跨森林注册(二)_第4张图片

在”安全“选项卡中添加森Hbyc.net中的Domain Users有如下图权限。

Windows 2008 R2之三十六ADCS实现跨森林注册(二)_第5张图片

分布复制的证书。

Windows 2008 R2之三十六ADCS实现跨森林注册(二)_第6张图片

6、设置CA的扩展属性如下图:

Windows 2008 R2之三十六ADCS实现跨森林注册(二)_第7张图片

7、在DCSRVB上,将计算机DCSRV加入到组Cert Publishers.

8、在DCSRV上运行

certutil –config dcsrv\hbsycsrsj-dcsvr-ca –ca.cert c:\ca.cer

其中hbsycsrsj-dcsvr-ca为证书颁发机构的名称,ca.cer为导出的根CA证书。

将CA.CER复制到DCSRVB上。

9、在DCSRVB运行如下命令

certutil –dspublish –f c:\ca.cer rootca

certutil –dspublish –f c:\ca.cer ntauthca

10、复制以下脚本为Pkisync.ps1脚本文件

#
# This script allows updating PKI objects in Active Directory for the
# cross-forest certificate enrollment
#
#This sample script is not supported under any Microsoft standard support
#program or service. This sample script is provided AS IS without warranty of
#any kind. Microsoft further disclaims all implied warranties including,
#without limitation, any implied warranties of merchantability or of fitness
#for a particular purpose. The entire risk arising out of the use or
#performance of the sample scripts and documentation remains with you. In no
#event shall Microsoft, its authors, or anyone else involved in the creation,
#production, or delivery of the scripts be liable for any damages whatsoever
# (including, without limitation, damages for loss of business profits, business
#interruption, loss of business information, or other pecuniary loss) arising
#out of the use of or inability to use this sample script or documentation,
#even if Microsoft has been advised of the possibility of such damages.
#

# Command line variables
#
$SourceForestName = ""
$TargetForestName = ""
$SourceDC = ""
$TargetDC = ""

$ObjectType = "all"
$ObjectCN = $null

$DryRun = $FALSE
$DeleteOnly = $FALSE
$OverWrite = $FALSE

function ParseCommandLine()
{
    if (2 -gt $Script:args.Count)
    {
        write-warning "Not enough arguments"
        Usage
        exit 87
    }
   
    for($i = 0; $i -lt $Script:args.Count; $i++)
    {
        switch($Script:args[$i].ToLower())
        {
            -sourceforest
            {
                $i++
                $Script:SourceForestName = $Script:args[$i]
            }
            -targetforest
            {
                $i++
                $Script:TargetForestName = $Script:args[$i]
            }                
            -cn
            {
                $i++
                $Script:ObjectCN = $Script:args[$i]
            }
            -type
            {
                $i++
                $Script:ObjectType = $Script:args[$i].ToLower()
            }
            -f
            {
                $Script:OverWrite = $TRUE
            }
            -whatif
            {
                $Script:DryRun = $TRUE
            }
            -deleteOnly
            {
                $Script:DeleteOnly = $TRUE
            }
            -targetdc
            {
                $i++
                $Script:TargetDC = $Script:args[$i]
            }
            -sourcedc
            {
                $i++
                $Script:SourceDC = $Script:args[$i]
            }
            default
            {
                write-warning ("Unknown parameter: " + $Script:args[$i])
                Usage
                exit 87
            }
        }
    }
}

function Usage()
{
    write-host ""
    write-host "Script to copy or delete PKI objects (default is copy)"
    write-host ""
    write-host "  Copy Command:"
    write-host ""
    write-host "  .\PKISync.ps1 -sourceforest -targetforest [-sourceDC ] [-targetDC ] [-type [-cn ]] [-f] [-whatif]"
    write-host ""
    write-host "  Delete Command:"
    write-host ""
    write-host "  .\PKISync.ps1 -targetforest [-targetDC ] [-type [-cn ]] [-deleteOnly] [-whatif]"
    write-host ""
    write-host "-sourceforest           -- DNS of the forest to process object from"
    write-host "-targetforest           -- DNS of the forest to process object to"
    write-host "-sourcedc               -- DNS of the DC in the source forest to process object from"
    write-host "-targetdc               -- DNS of the DC in the target forest to process object to"
    write-host "-type                   -- Type of object to process, if omitted then all object types are processed"
    write-host "                           CA         -- Process CA object(s)"
    write-host "                           Template   -- Process Template object(s)"
    write-host "                           OID        -- Process OID object(s)"
    write-host '-cn                     -- Common name of the object to process, do not include the cn= (ie "User" and not "CN=User"'
    write-host "                           This option is only valid if -type <> is also specified"
    write-host "-f                      -- Force overwrite of existing objects when copying. Ignored when deleting."
    write-host "-whatif                 -- Display what object(s) will be processed without processing"
    write-host "-deleteOnly             -- Will delete object in the target forest if it exists"           
    write-host ""
    write-host ""   
}

#
# Build a list of attributes to copy for some object type
#
function GetSchemaSystemMayContain($ForestContext, $ObjectType)
{
    #
    # first get all attributes that are part of systemMayContain list
    #
    $SchemaDE = [System.DirectoryServices.ActiveDirectory.ActiveDirectorySchemaClass]::FindByName($ForestContext, $ObjectType).GetDirectoryEntry()
    $SystemMayContain = $SchemaDE.systemMayContain

    #
    # if schema was upgraded with adprep.exe, we need to check mayContain list as well
    #
    if($null -ne $SchemaDE.mayContain)
    {
        $MayContain = $SchemaDE.mayContain
        foreach($attr in $MayContain)
        {
            $SystemMayContain.Add($attr)
        }
    }
       
    #
    # special case some of the inherited attributes
    #
    if (-1 -eq $SystemMayContain.IndexOf("displayName"))
    {
        $SystemMayContain.Add("displayName")
    }
    if (-1 -eq $SystemMayContain.IndexOf("flags"))
    {
        $SystemMayContain.Add("flags")
    }
    if ($objectType.ToLower().Contains("template") -and -1 -eq $SystemMayContain.IndexOf("revision"))
    {
        $SystemMayContain.Add("revision")
    }
   
    return $SystemMayContain
}

#
# Copy or delete all objects of some type
#
function ProcessAllObjects($SourcePKIServicesDE, $TargetPKIServicesDE, $RelativeDN)
{
    $SourceObjectsDE = $SourcePKIServicesDE.psbase.get_Children().find($RelativeDN)
    $ObjectCN = $null
   
    foreach($ChildNode in $SourceObjectsDE.psbase.get_Children())
    {
        # if some object failed, we will try to continue with the rest
        trap
        {
            # CN maybe null here, but its ok. Doing best effort.
            write-warning ("Error while coping an object. CN=" + $ObjectCN)
            write-warning $_
            write-warning $_.InvocationInfo.PositionMessage
            continue
        }

        $ObjectCN = $ChildNode.psbase.Properties["cn"]
        ProcessObject $SourcePKIServicesDE $TargetPKIServicesDE $RelativeDN $ObjectCN
        $ObjectCN = $null
    }
   
}

#
# Copy or delete an object
#
function ProcessObject($SourcePKIServicesDE, $TargetPKIServicesDE, $RelativeDN, $ObjectCN)
{
    $SourceObjectContainerDE = $SourcePKIServicesDE.psbase.get_Children().find($RelativeDN)
    $TargetObjectContainerDE = $TargetPKIServicesDE.psbase.get_Children().find($RelativeDN)

    #
    # when copying make sure there is an object to copy
    #
    if($FALSE -eq $Script:DeleteOnly)
    {
        $DSSearcher =  [System.DirectoryServices.DirectorySearcher]$SourceObjectContainerDE
        $DSSearcher.Filter = "(cn=" +$ObjectCN+")"
        $SearchResult = $DSSearcher.FindAll()
        if (0 -eq $SearchResult.Count)
        {
            write-host ("Source object does not exist: CN=" + $ObjectCN + "," + $RelativeDN)
            return
        }
        $SourceObjectDE = $SourceObjectContainerDE.psbase.get_Children().find("CN=" + $ObjectCN)
    }
   
    #
    # Check to see if the target object exists, if it does delete if overwrite is enabled.
    # Also delete is this a deletion only operation.
    #
    $DSSearcher =  [System.DirectoryServices.DirectorySearcher]$TargetObjectContainerDE
    $DSSearcher.Filter = "(cn=" +$ObjectCN+")"
    $SearchResult = $DSSearcher.FindAll()
    if ($SearchResult.Count -gt 0)
    {
        $TargetObjectDE = $TargetObjectContainerDE.psbase.get_Children().find("CN=" + $ObjectCN)

        if($Script:DeleteOnly)
        {
            write-host ("Deleting: " + $TargetObjectDE.DistinguishedName)
            if($FALSE -eq $DryRun)
            {
                $TargetObjectContainerDE.psbase.get_Children().Remove($TargetObjectDE)
            }
            return
        }
        elseif ($Script:OverWrite)
        {
            write-host ("OverWriting: " + $TargetObjectDE.DistinguishedName)
            if($FALSE -eq $DryRun)
            {
                $TargetObjectContainerDE.psbase.get_Children().Remove($TargetObjectDE)
            }
        }
        else
        {
            write-warning ("Object exists, use -f to overwrite. Object: " + $TargetObjectDE.DistinguishedName)
            return
        }
    }
    else
    {
        if($Script:DeleteOnly)
        {
            write-warning ("Can't delete object. Object doesn't exist. Object: " + $ObjectCN + ", " + $TargetObjectContainerDE.DistinguishedName)
            return
        }
        else
        {       
            write-host ("Copying Object: " + $SourceObjectDE.DistinguishedName)
        }
    }
   
    #
    # Only update the object if this is not a dry run
    #
    if($FALSE -eq $DryRun -and $FALSE -eq $Script:DeleteOnly)
    {
        #Create new AD object  
        $NewDE = $TargetObjectContainerDE.psbase.get_Children().Add("CN=" + $ObjectCN, $SourceObjectDE.psbase.SchemaClassName)

        #Obtain systemMayContain for the object type from the AD schema
        $ObjectMayContain = GetSchemaSystemMayContain $SourceForestContext $SourceObjectDE.psbase.SchemaClassName
        #Copy attributes defined in the systemMayContain for the object type
        foreach($Attribute in $ObjectMayContain)
        {
            $AttributeValue = $SourceObjectDE.psbase.Properties[$Attribute].Value
            if ($null -ne $AttributeValue)
            {
                $NewDE.psbase.Properties[$Attribute].Value = $AttributeValue
                $NewDE.psbase.CommitChanges()
            }
        }
        #Copy secuirty descriptor to new object. Only DACL is copied.
        $BinarySecurityDescriptor = $SourceObjectDE.psbase.ObjectSecurity.GetSecurityDescriptorBinaryForm()
        $NewDE.psbase.ObjectSecurity.SetSecurityDescriptorBinaryForm($BinarySecurityDescriptor, [System.Security.AccessControl.AccessControlSections]::Access)
        $NewDE.psbase.CommitChanges()
    }
}

#
# Get parent container for all PKI objects in the AD
#
function GetPKIServicesContainer([System.DirectoryServices.ActiveDirectory.DirectoryContext] $ForestContext, $dcName)
{
    $ForObj = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
    $DE = $ForObj.RootDomain.GetDirectoryEntry()
   
    if("" -ne $dcName)
    {
        $newPath = [System.Text.RegularExpressions.Regex]::Replace($DE.psbase.Path, "LDAP://\S*/", "LDAP://" + $dcName + "/")
        $DE = New-Object System.DirectoryServices.DirectoryEntry $newPath
    }

    $PKIServicesContainer = $DE.psbase.get_Children().find("CN=Public Key Services,CN=Services,CN=Configuration")
    return $PKIServicesContainer
}

#########################################################
# Main script code
#########################################################

#
# All errors are fatal by default unless there is another 'trap' with 'continue'
#
trap
{
    write-error "The script has encoutnered a fatal error. Terminating script."
    break
}

ParseCommandLine

#
# Get a hold of the containers in each forest
#
write-host ("Target Forest: " + $TargetForestName.ToUpper())
$TargetForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext Forest, $TargetForestName
$TargetPKIServicesDE = GetPKIServicesContainer $TargetForestContext $Script:TargetDC

# Only need source forest when copying
if($FALSE -eq $Script:DeleteOnly)
{
    write-host ("Source Forest: " + $SourceForestName.ToUpper())
    $SourceForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext Forest, $SourceForestName
    $SourcePKIServicesDE = GetPKIServicesContainer $SourceForestContext $Script:SourceDC
}
else
{
    $SourcePKIServicesDE = $TargetPKIServicesDE
}

if("" -ne $ObjectType) {write-host ("Object Category to process: " + $ObjectType.ToUpper())}

#
# Process the command
#
switch($ObjectType.ToLower())
{
    all
    {
        write-host ("Enrollment Serverices Container")
    ProcessAllObjects $SourcePKIServicesDE $TargetPKIServicesDE "CN=Enrollment Services"
        write-host ("Certificate Templates Container")
        ProcessAllObjects $SourcePKIServicesDE $TargetPKIServicesDE "CN=Certificate Templates"
        write-host ("OID Container")
        ProcessAllObjects $SourcePKIServicesDE $TargetPKIServicesDE "CN=OID"
    }
    ca
    {
        if($null -eq $ObjectCN)
        {
            ProcessAllObjects $SourcePKIServicesDE $TargetPKIServicesDE "CN=Enrollment Services"
        }
        else
        {
            ProcessObject $SourcePKIServicesDE $TargetPKIServicesDE "CN=Enrollment Services" $ObjectCN
        }
    }
    oid
    {
        if($null -eq $ObjectCN)
        {
            ProcessAllObjects $SourcePKIServicesDE $TargetPKIServicesDE "CN=OID"
        }
        else
        {
            ProcessObject $SourcePKIServicesDE $TargetPKIServicesDE "CN=OID" $ObjectCN
        }
    }
    template
    {
        if($null -eq $ObjectCN)
        {
            ProcessAllObjects $SourcePKIServicesDE $TargetPKIServicesDE "CN=Certificate Templates"
        }
        else
        {
            ProcessObject $SourcePKIServicesDE $TargetPKIServicesDE "CN=Certificate Templates" $ObjectCN
        }   
    }
    default
    {
        write-warning ("Unknown object type: " + $ObjectType.ToLower())
        Usage
        exit 87
    }
}

 

11、在DCSRVB打开PowerSell运行以下命令

set-ececutionpolicy unrestricted

pkisync.ps1 –sourceforest hbsycsrsj.com –targetforest hbyc.net –f

12、打开组策略编辑器,编辑默认的策略。如下图

Windows 2008 R2之三十六ADCS实现跨森林注册(二)_第8张图片

13、重启计算机dcsrv和dcsrvB.。在DCsrvB运行Certmgr.msc,申请证书成功。