命令

查看当前用户的受信任根证书:

1
certutil -user -store Root

为了兼容性,还需要查看本地计算机的受信任根证书:

1
certutil -store Root

导入证书到当前用户的受信任根证书:

1
certutil -user -addstore Root ca.crt

其中 ca.crt为需要导入的根证书。

代码

实际代码中还要考虑cmd输出的内容可能不是UFT-8编码,而是GB2312编码。此时为了日志可读性,需要进行编码转换。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
// CertImport 自动导入证书
// 先判断证书是否已经存在,如果存在则不导入
// 否则导入证书
func CertImport() {
	logger.Info("开始导入证书")
	cmd := exec.Command("certutil", "-user", "-store", "Root")
	cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	bytesOut := bytes.NewBuffer([]byte{})
	cmd.Stdout = bytesOut
	err := cmd.Run()
	if err != nil {
		logger.Errorf("certutil -user -store Root执行报错:%v", errors.WithStack(err))
		return
	}
	exist := false
	var outData []byte
	outData, err = gb2312ToUTF8(bytesOut.Bytes())
	if err != nil {
		logger.Errorf("gb2312转utf8报错:%v", errors.WithStack(err))
		return
	}
	if bytes.Contains(outData, []byte("macdfree@163.com")) {
		exist = true
		logger.Infof("证书已经存在或命令执行报错:%s", string(outData))
	}
	if !exist {
		cmd = exec.Command("certutil", "-store", "Root")
		cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
		bytesOut = bytes.NewBuffer([]byte{})
		cmd.Stdout = bytesOut
		err = cmd.Run()
		if err != nil {
			logger.Errorf("certutil -store Root执行报错:%v", errors.WithStack(err))
			return
		}
		outData, err = gb2312ToUTF8(bytesOut.Bytes())
		if err != nil {
			logger.Errorf("gb2312转utf8报错:%v", errors.WithStack(err))
			return
		}
		if bytes.Contains(outData, []byte("macdfree@163.com")) {
			exist = true
			logger.Infof("证书已经存在或命令执行报错:%s", string(outData))
		}
	}
	if !exist {
		cmd := exec.Command("certutil", "-user", "-addstore", "Root", "ca.crt")
		cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
		bytesOut = bytes.NewBuffer([]byte{})
		cmd.Stdout = bytesOut
		err = cmd.Run()
		if err != nil {
			logger.Errorf("certutil -user -addstore Root ca.crt执行报错:%v", errors.WithStack(err))
			return
		}
		outData, err = gb2312ToUTF8(bytesOut.Bytes())
		if err != nil {
			logger.Errorf("gb2312转utf8报错:%v", errors.WithStack(err))
			return
		}
		logger.Infof("自动导入证书执行结果:%s", string(outData))
	}
}

func isGB2312(data []byte) bool {
	i := 0
	count := 0
	for i < len(data) {
		// 如果第一个字节的范围在0xA1到0xF7
		if data[i] >= 0xA1 && data[i] <= 0xF7 {
			// 检查第二个字节是否在0xA1到0xFE范围内
			if i+1 < len(data) && data[i+1] >= 0xA1 && data[i+1] <= 0xFE {
				count++
				i += 2
			} else {
				i++
			}
		} else {
			i++
		}
	}

	logger.Infof("检测GB2312编码,匹配数为:%d", count)
	if count > 2 {
		return true
	} else {
		return false
	}
}

func gb2312ToUTF8(gb2312Bytes []byte) ([]byte, error) {
	if !isGB2312(gb2312Bytes) {
		logger.Infof("不是GB2312编码,直接返回:%s", string(gb2312Bytes))
		return gb2312Bytes, nil
	}
	transformer := simplifiedchinese.GB18030.NewDecoder()

	// 使用transformer将GB2312解码为UTF-8
	utf8Bytes, _, err := transform.Bytes(transformer, gb2312Bytes)
	if err != nil {
		return nil, err
	}
	return utf8Bytes, nil
}

参考

https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/certutil