工作機平日使用本機帳號登入,存取伺服器或網站時才使用網域(AD)帳號,為了避免每次都重新輸入,我會在第一次登入時使用Windows內建的密碼記憶功能把網域帳號密碼存起來。公司的伺服器及網站眾多,於是乎就出現一張長長的密碼記憶清單...
問題來了! 基於資安管控,網域帳號的密碼需要定期更換,但換過密碼後要是忘了更新上述的記憶密碼,下回想連上某台伺服器,Windows便會拿著舊密碼試著登入... 登楞! 很快地,連錯三次,帳號上鎖 orz 必須連絡網管才能解鎖。
因此,每回改完網域帳號密碼,得趕快更新記憶密碼。要像下圖逐一點開每台伺服器/網站項目,按下【Edit】修改密碼、儲存,同樣動作要重覆十多次,而且每三個月需要重頭演練一次。雖然對正常人來說,操作十來次根本不算什麼,但,動作重複是程式設計功力不足的象徵,對程式魔人是一種羞辱。不行! 重複操作正在毁滅我的人生(謎之聲: 有這麼嚴重? 施主,你病得不輕呀!),如果沒法讓它自動化,我就退出江湖。
認真研究後,找到Windows有個內建命令列工具,cmdkey,可用來管理記憶密碼。
既然有工具,理論上就有對應的Windows API,但已有現成cmdkey.exe可用,透過Process呼叫外部程式的把戲也難不倒我,就不花功夫去研究API了,寫支C#程式,先輸入網域帳號(Domain\Account)及新密碼,呼叫cmdkey /list列出所有儲存密碼,從中比對User名稱找出屬於該帳號的項目,再逐一呼叫cmdkey /add:server_name /user:Domain\Account /pass:newPassword變更成新密碼,就大功告成囉!
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace UpdateStoredPwd
{
class Program
{
staticvoid Main(string[] args)
{
Regex reTarget = new Regex("Target: (?<t>.+):target=(?<s>.+)");
Regex reUser = new Regex("User: (?<u>.+)");
Console.Write("User: ");
string account = Console.ReadLine();
Console.Write("Password: ");
Console.ForegroundColor = ConsoleColor.Black;
string pwd = Console.ReadLine();
Console.ForegroundColor = ConsoleColor.White;
bool catchAccount = false;
string currTarget = null, currType = null;
foreach (string line in Execute("cmdkey", "/list")
.Replace("\r", string.Empty).Split('\n'))
{
if (!catchAccount)
{
var m = reTarget.Match(line);
if (m.Success)
{
catchAccount = true;
currTarget = m.Groups["s"].Value;
currType = m.Groups["t"].Value;
}
}
else
{
var m = reUser.Match(line);
if (m.Success)
{
catchAccount = false;
string user = m.Groups["u"].Value;
if (string.Compare(user, account, true) == 0)
{
string a = string.Format(
"/{3}:{0} /user:{1} /pass:{2}", currTarget, account,
pwd, currType == "Domain" ? "add" : "generic");
Console.WriteLine("cmdkey " +
a.Replace("pass:" + pwd, "pass:*****"));
string res = Program.Execute("cmdkey", a);
Console.WriteLine(res);
}
}
}
}
Console.Read();
}
staticstring Execute(string prog, string args)
{
Process p = new Process();
p.StartInfo = new ProcessStartInfo(prog, args);
//必須要設定以下兩個屬性才可將輸出結果導向
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
//不顯示任何視窗
p.StartInfo.CreateNoWindow = true;
p.Start();
StringBuilder sb = new StringBuilder();
while (!p.HasExited)
{
string line = p.StandardOutput.ReadLine();
sb.AppendLine(line);
}
return sb.ToString();
}
}
}
【後記】
猜想已有軟體能做類似的批次密碼修改,但初步搜尋沒找到,就決定把尋找時間省下來自寫兼練功(謎: 明明就是手癢所以不認真找),但若有人知道有相關軟體可用,歡迎分享。另外,也找到PowerShell呼叫API更改記憶密碼的範例,但寫慣C#硬要用PowerShell有種舒馬克跑去開飛機的彆腳感,最後還是抄起C#就打,呵!