Skip to content

Commit a4df431

Browse files
committed
add cert install on win
leverage windows syscalls to install our CA certificate in the user's trusted keystore code heavily inspired by https://github.com/FiloSottile/mkcert
1 parent 0a5f85d commit a4df431

File tree

2 files changed

+105
-1
lines changed

2 files changed

+105
-1
lines changed

certificates/install_default.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// You should have received a copy of the GNU Affero General Public License
1414
// along with this program. If not, see <https://www.gnu.org/licenses/>.
1515

16-
//go:build !darwin
16+
//go:build !darwin && !windows
1717

1818
package certificates
1919

certificates/install_windows.go

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright 2023 Arduino SA
2+
//
3+
// This program is free software: you can redistribute it and/or modify
4+
// it under the terms of the GNU Affero General Public License as published
5+
// by the Free Software Foundation, either version 3 of the License, or
6+
// (at your option) any later version.
7+
//
8+
// This program is distributed in the hope that it will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
// GNU Affero General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU Affero General Public License
14+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
15+
16+
// code inspired by https://github.com/FiloSottile/mkcert licenced under BSD3
17+
18+
package certificates
19+
20+
import (
21+
"encoding/pem"
22+
"fmt"
23+
"io/ioutil"
24+
"syscall"
25+
"unsafe"
26+
27+
"github.com/arduino/go-paths-helper"
28+
log "github.com/sirupsen/logrus"
29+
)
30+
31+
var (
32+
modcrypt32 = syscall.NewLazyDLL("crypt32.dll")
33+
procCertAddEncodedCertificateToStore = modcrypt32.NewProc("CertAddEncodedCertificateToStore")
34+
procCertCloseStore = modcrypt32.NewProc("CertCloseStore")
35+
procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW")
36+
)
37+
38+
// // InstallCertificates will install the certificates in the system keychain on windows
39+
func InstallCertificate(certFile *paths.Path) {
40+
// Load cert
41+
cert, err := ioutil.ReadFile(certFile.String())
42+
if err != nil {
43+
log.Errorf("failed to read root certificate: %s", err)
44+
}
45+
// Decode PEM
46+
if certBlock, _ := pem.Decode(cert); certBlock == nil || certBlock.Type != "CERTIFICATE" {
47+
log.Error("invalid PEM data: decode pem")
48+
} else {
49+
cert = certBlock.Bytes
50+
}
51+
// Open root store
52+
store, err := openWindowsRootStore()
53+
if err != nil {
54+
log.Errorf("cannot open root store %s", err)
55+
} else {
56+
log.Info("opened Root Store")
57+
}
58+
defer store.close()
59+
// Add cert
60+
err = store.addCert(cert)
61+
if err != nil {
62+
log.Errorf("cannot install certificate in the system keychain: %s", err)
63+
} else {
64+
log.Info("certificate installed")
65+
}
66+
}
67+
68+
type windowsRootStore uintptr
69+
70+
func openWindowsRootStore() (windowsRootStore, error) {
71+
rootStr, err := syscall.UTF16PtrFromString("ROOT")
72+
if err != nil {
73+
return 0, err
74+
}
75+
store, _, err := procCertOpenSystemStoreW.Call(0, uintptr(unsafe.Pointer(rootStr)))
76+
if store != 0 {
77+
return windowsRootStore(store), nil
78+
}
79+
return 0, fmt.Errorf("failed to open windows root store: %s", err)
80+
}
81+
82+
func (w windowsRootStore) close() error {
83+
ret, _, err := procCertCloseStore.Call(uintptr(w), 0)
84+
if ret != 0 {
85+
return nil
86+
}
87+
return fmt.Errorf("failed to close windows root store: %s", err)
88+
}
89+
90+
func (w windowsRootStore) addCert(cert []byte) error {
91+
// this will always override
92+
ret, _, err := procCertAddEncodedCertificateToStore.Call(
93+
uintptr(w), // HCERTSTORE hCertStore
94+
uintptr(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING), // DWORD dwCertEncodingType
95+
uintptr(unsafe.Pointer(&cert[0])), // const BYTE *pbCertEncoded
96+
uintptr(len(cert)), // DWORD cbCertEncoded
97+
3, // DWORD dwAddDisposition (CERT_STORE_ADD_REPLACE_EXISTING is 3)
98+
0, // PCCERT_CONTEXT *ppCertContext
99+
)
100+
if ret != 0 {
101+
return nil
102+
}
103+
return fmt.Errorf("failed adding cert: %s", err)
104+
}

0 commit comments

Comments
 (0)