续Windows 2008 R2之三十五ADCS实现跨森林注册(一)
5、复制”用户“证书模板为”跨森林用户证书“,相关设设置如下图
打开证书颁发机构,右击”证书模板“选择”管理“
右击”用户“证书,选择”复制“
在”安全“选项卡中添加森Hbyc.net中的Domain Users有如下图权限。
分布复制的证书。
6、设置CA的扩展属性如下图:
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 <SourceForestDNS> -targetforest <TargetForestDNS> [-sourceDC <SourceDCDNS>] [-targetDC <TargetDCDNS>] [-type <CA|Template|OID> [-cn <ObjectCN>]] [-f] [-whatif]"
write-host ""
write-host " Delete Command:"
write-host ""
write-host " .\PKISync.ps1 -targetforest <TargetForestDNS> [-targetDC <TargetDCDNS>] [-type <CA|Template|OID> [-cn <ObjectCN>]] [-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、打开组策略编辑器,编辑默认的策略。如下图
13、重启计算机dcsrv和dcsrvB.。在DCsrvB运行Certmgr.msc,申请证书成功。