You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

非域计算机中域用户:注册表添加凭据及PowerShell脚本调用方法

如何在非域加入的电脑上通过注册表存储凭据并在PowerShell中调用用于New-PSDrive

首先得说清楚:不建议手动直接编辑注册表来添加凭据,因为Windows存储的凭据是加密过的,手动写入的注册表项大概率无法被系统正常识别和使用。更简单可靠的方式是用系统自带的cmdkey命令来添加,它会自动把凭据写入正确的注册表位置,还能保证加密格式正确。不过我还是会把注册表的具体位置和读取方法都讲清楚。

一、注册表中存储凭据的位置

Windows针对当前用户的凭据(包括非域环境下的域用户凭据),会存在这个路径下:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Credentials

这个路径下的每个子键都是一个用GUID命名的项,每个项对应一个存储的凭据。每个GUID子键里包含TargetName(要访问的目标资源名)、UserName(完整的域/用户名)、CredentialBlob(加密后的密码)等关键值。

但还是那句话:别手动创建这些键值对,用cmdkey更省心。

二、添加凭据(推荐用cmdkey)

打开PowerShell或者命令提示符,执行以下命令:

cmdkey /add:你的目标资源名 /user:你的域用户名 /pass:你的密码

举个例子,如果你要访问的文件服务器是fileserver01,域用户名是CORP\mateusz,密码是P@ssw0rd123,命令就是:

cmdkey /add:fileserver01 /user:CORP\mateusz /pass:P@ssw0rd123

执行完这个命令,凭据就会自动加密存储到上面说的注册表路径里了,而且系统能直接识别使用。

三、从注册表读取凭据到PowerShell变量

因为凭据是加密存储的,不能直接从注册表读取明文密码,需要用Windows的加密API来解密。我写了一个PowerShell函数来帮你完成这个操作:

# 导入解密所需的Windows API
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class CredentialHelper {
    [DllImport("crypt32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool CryptUnprotectData(
        ref DATA_BLOB pDataIn,
        out string szDataDescr,
        ref DATA_BLOB pOptionalEntropy,
        IntPtr pvReserved,
        IntPtr pPromptStruct,
        int dwFlags,
        ref DATA_BLOB pDataOut);
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct DATA_BLOB {
        public int cbData;
        public IntPtr pbData;
    }
}
"@

# 定义读取存储凭据的函数
function Get-SavedCredential {
    param(
        [string]$Target  # 要匹配的目标资源名,就是你用cmdkey添加时的/add参数值
    )
    $credPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Credentials"
    $credKeys = Get-ChildItem -Path $credPath -ErrorAction SilentlyContinue

    foreach ($key in $credKeys) {
        $targetName = Get-ItemProperty -Path $key.PSPath -Name "TargetName" -ErrorAction SilentlyContinue
        if ($targetName -and $targetName.TargetName -eq $Target) {
            # 获取用户名和加密的密码Blob
            $userName = (Get-ItemProperty -Path $key.PSPath -Name "UserName").UserName
            $blob = (Get-ItemProperty -Path $key.PSPath -Name "CredentialBlob").CredentialBlob

            # 准备解密所需的Blob结构
            $dataIn = New-Object CredentialHelper+DATA_BLOB
            $dataIn.cbData = $blob.Length
            $dataIn.pbData = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($blob.Length)
            [System.Runtime.InteropServices.Marshal]::Copy($blob, 0, $dataIn.pbData, $blob.Length)
            
            $dataOut = New-Object CredentialHelper+DATA_BLOB
            $optionalEntropy = New-Object CredentialHelper+DATA_BLOB
            $optionalEntropy.cbData = 0
            $optionalEntropy.pbData = [IntPtr]::Zero

            # 调用API解密密码
            if ([CredentialHelper]::CryptUnprotectData([ref]$dataIn, [ref]$null, [ref]$optionalEntropy, [IntPtr]::Zero, [IntPtr]::Zero, 0, [ref]$dataOut)) {
                $password = [System.Text.UnicodeEncoding]::Unicode.GetString([System.Runtime.InteropServices.Marshal]::Copy($dataOut.pbData, 0, $dataOut.cbData))
                # 释放内存
                [System.Runtime.InteropServices.Marshal]::FreeHGlobal($dataIn.pbData)
                [System.Runtime.InteropServices.Marshal]::FreeHGlobal($dataOut.pbData)
                # 返回PSCredential对象
                return New-Object System.Management.Automation.PSCredential($userName, (ConvertTo-SecureString $password -AsPlainText -Force))
            } else {
                Write-Error "解密凭据失败,请检查当前用户权限。"
                return $null
            }
        }
    }
    Write-Error "未找到目标为[$Target]的存储凭据。"
    return $null
}

使用这个函数读取凭据到变量:

# 替换成你添加的目标资源名
$myCredential = Get-SavedCredential -Target "fileserver01"

四、用变量执行New-PSDrive命令

拿到$myCredential变量后,直接在New-PSDrive里指定即可:

New-PSDrive -Name "Z" -PSProvider "FileSystem" -Root "\\fileserver01\shared" -Credential $myCredential

更省心的方式:

其实如果你已经用cmdkey添加了凭据,执行New-PSDrive的时候可以不用显式指定-Credential参数,系统会自动匹配存储的凭据,直接运行下面的命令就行:

New-PSDrive -Name "Z" -PSProvider "FileSystem" -Root "\\fileserver01\shared"

重要注意事项

  • 因为你的电脑没加入域,用户名必须写成完整的域格式(比如DOMAIN\username),不能只写用户名,否则系统无法识别。
  • 存储在注册表的凭据只有当前登录用户能访问和解密,其他用户无法使用。
  • 虽然密码是加密存储的,但也要注意保护你的用户账户安全,避免他人登录你的账户获取凭据。

内容的提问来源于stack exchange,提问作者Mateusz D

火山引擎 最新活动