From 5b63ef50a6264a9749823f4e41da6d4e7836e419 Mon Sep 17 00:00:00 2001 From: Andy Zhao Date: Fri, 16 Jun 2023 13:42:01 -0700 Subject: [PATCH] feat: Introduce windows-specific client (#83) Also consolidates 3 config utils into a single common util. --- build/scripts/windows_amd64.ps1 | 2 +- go.mod | 6 +- go.sum | 4 ++ internal/signer/darwin/signer.go | 2 +- .../util/test_data/certificate_config.json | 8 --- internal/signer/darwin/util/util.go | 55 ------------------ internal/signer/darwin/util/util_test.go | 29 ---------- internal/signer/linux/signer.go | 2 +- .../util/test_data/certificate_config.json | 10 ---- .../util/test_data/certificate_config.json | 19 ++++++ internal/signer/{linux => }/util/util.go | 21 +++++-- internal/signer/{linux => }/util/util_test.go | 25 +++++++- internal/signer/windows/go.mod | 8 --- internal/signer/windows/go.sum | 11 ---- internal/signer/windows/signer.go | 5 +- .../util/test_data/certificate_config.json | 9 --- internal/signer/windows/util/util.go | 57 ------------------ internal/signer/windows/util/util_test.go | 37 ------------ linux/client.go | 2 +- windows/client.go | 58 +++++++++++++++++++ 20 files changed, 133 insertions(+), 237 deletions(-) delete mode 100644 internal/signer/darwin/util/test_data/certificate_config.json delete mode 100644 internal/signer/darwin/util/util.go delete mode 100644 internal/signer/darwin/util/util_test.go delete mode 100644 internal/signer/linux/util/test_data/certificate_config.json create mode 100644 internal/signer/util/test_data/certificate_config.json rename internal/signer/{linux => }/util/util.go (73%) rename internal/signer/{linux => }/util/util_test.go (65%) delete mode 100644 internal/signer/windows/go.mod delete mode 100644 internal/signer/windows/go.sum delete mode 100644 internal/signer/windows/util/test_data/certificate_config.json delete mode 100644 internal/signer/windows/util/util.go delete mode 100644 internal/signer/windows/util/util_test.go create mode 100644 windows/client.go diff --git a/build/scripts/windows_amd64.ps1 b/build/scripts/windows_amd64.ps1 index d259535..32fca72 100644 --- a/build/scripts/windows_amd64.ps1 +++ b/build/scripts/windows_amd64.ps1 @@ -23,7 +23,7 @@ If (Test-Path $OutputFolder) { # Build the signer binary Set-Location .\internal\signer\windows go build -Move-Item .\signer.exe ..\..\..\build\bin\windows_amd64\ecp.exe +Move-Item .\windows.exe ..\..\..\build\bin\windows_amd64\ecp.exe Set-Location ..\..\..\ # Build the signer library diff --git a/go.mod b/go.mod index 93a15e7..0bef8f9 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,8 @@ module github.com/googleapis/enterprise-certificate-proxy go 1.19 -require github.com/google/go-pkcs11 v0.2.0 +require ( + github.com/google/go-pkcs11 v0.2.0 + golang.org/x/crypto v0.10.0 + golang.org/x/sys v0.9.0 +) diff --git a/go.sum b/go.sum index 2437797..1370364 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,6 @@ github.com/google/go-pkcs11 v0.2.0 h1:5meDPB26aJ98f+K9G21f0AqZwo/S5BJMJh8nuhMbdsI= github.com/google/go-pkcs11 v0.2.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= +golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/internal/signer/darwin/signer.go b/internal/signer/darwin/signer.go index 5bd5e24..84f2158 100644 --- a/internal/signer/darwin/signer.go +++ b/internal/signer/darwin/signer.go @@ -29,7 +29,7 @@ import ( "time" "github.com/googleapis/enterprise-certificate-proxy/internal/signer/darwin/keychain" - "github.com/googleapis/enterprise-certificate-proxy/internal/signer/darwin/util" + "github.com/googleapis/enterprise-certificate-proxy/internal/signer/util" ) // If ECP Logging is enabled return true diff --git a/internal/signer/darwin/util/test_data/certificate_config.json b/internal/signer/darwin/util/test_data/certificate_config.json deleted file mode 100644 index a4f0edf..0000000 --- a/internal/signer/darwin/util/test_data/certificate_config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "cert_configs": { - "macos_keychain": { - "issuer": "Google Endpoint Verification" - } - } -} - diff --git a/internal/signer/darwin/util/util.go b/internal/signer/darwin/util/util.go deleted file mode 100644 index b8019d8..0000000 --- a/internal/signer/darwin/util/util.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2022 Google LLC. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package util provides helper functions for the signer. -package util - -import ( - "encoding/json" - "io" - "os" -) - -// EnterpriseCertificateConfig contains parameters for initializing signer. -type EnterpriseCertificateConfig struct { - CertConfigs CertConfigs `json:"cert_configs"` -} - -// CertConfigs is a container for various ECP Configs. -type CertConfigs struct { - MacOSKeychain MacOSKeychain `json:"macos_keychain"` -} - -// MacOSKeychain contains parameters describing the certificate to use. -type MacOSKeychain struct { - Issuer string `json:"issuer"` -} - -// LoadConfig retrieves the ECP config file. -func LoadConfig(configFilePath string) (config EnterpriseCertificateConfig, err error) { - jsonFile, err := os.Open(configFilePath) - if err != nil { - return EnterpriseCertificateConfig{}, err - } - - byteValue, err := io.ReadAll(jsonFile) - if err != nil { - return EnterpriseCertificateConfig{}, err - } - err = json.Unmarshal(byteValue, &config) - if err != nil { - return EnterpriseCertificateConfig{}, err - } - return config, nil - -} diff --git a/internal/signer/darwin/util/util_test.go b/internal/signer/darwin/util/util_test.go deleted file mode 100644 index 372ef7e..0000000 --- a/internal/signer/darwin/util/util_test.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2022 Google LLC. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "testing" -) - -func TestLoadConfig(t *testing.T) { - config, err := LoadConfig("./test_data/certificate_config.json") - if err != nil { - t.Errorf("LoadConfig error: %q", err) - } - want := "Google Endpoint Verification" - if config.CertConfigs.MacOSKeychain.Issuer != want { - t.Errorf("Expected issuer is %q, got: %q", want, config.CertConfigs.MacOSKeychain.Issuer) - } -} diff --git a/internal/signer/linux/signer.go b/internal/signer/linux/signer.go index 40f9e42..8248025 100644 --- a/internal/signer/linux/signer.go +++ b/internal/signer/linux/signer.go @@ -30,7 +30,7 @@ import ( "time" "github.com/googleapis/enterprise-certificate-proxy/internal/signer/linux/pkcs11" - "github.com/googleapis/enterprise-certificate-proxy/internal/signer/linux/util" + "github.com/googleapis/enterprise-certificate-proxy/internal/signer/util" ) // If ECP Logging is enabled return true diff --git a/internal/signer/linux/util/test_data/certificate_config.json b/internal/signer/linux/util/test_data/certificate_config.json deleted file mode 100644 index 64ed1c2..0000000 --- a/internal/signer/linux/util/test_data/certificate_config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "cert_configs": { - "pkcs11": { - "slot": "0x1739427", - "label": "gecc", - "user_pin": "0000", - "module": "pkcs11_module.so" - } - } -} diff --git a/internal/signer/util/test_data/certificate_config.json b/internal/signer/util/test_data/certificate_config.json new file mode 100644 index 0000000..d693c51 --- /dev/null +++ b/internal/signer/util/test_data/certificate_config.json @@ -0,0 +1,19 @@ +{ + "cert_configs": { + "macos_keychain": { + "issuer": "Google Endpoint Verification" + }, + "windows_store": { + "issuer": "enterprise_v1_corp_client", + "store": "MY", + "provider": "current_user" + }, + "pkcs11": { + "slot": "0x1739427", + "label": "gecc", + "user_pin": "0000", + "module": "pkcs11_module.so" + } + } +} + diff --git a/internal/signer/linux/util/util.go b/internal/signer/util/util.go similarity index 73% rename from internal/signer/linux/util/util.go rename to internal/signer/util/util.go index c740b31..1ae1daf 100644 --- a/internal/signer/linux/util/util.go +++ b/internal/signer/util/util.go @@ -25,12 +25,26 @@ type EnterpriseCertificateConfig struct { CertConfigs CertConfigs `json:"cert_configs"` } -// CertConfigs is a container for various ECP Configs. +// CertConfigs is a container for various OS-specific ECP Configs. type CertConfigs struct { - PKCS11 PKCS11 `json:"pkcs11"` + MacOSKeychain MacOSKeychain `json:"macos_keychain"` + WindowsStore WindowsStore `json:"windows_store"` + PKCS11 PKCS11 `json:"pkcs11"` } -// PKCS11 contains parameters describing the certificate to use. +// MacOSKeychain contains keychain parameters describing the certificate to use. +type MacOSKeychain struct { + Issuer string `json:"issuer"` +} + +// WindowsStore contains Windows key store parameters describing the certificate to use. +type WindowsStore struct { + Issuer string `json:"issuer"` + Store string `json:"store"` + Provider string `json:"provider"` +} + +// PKCS11 contains PKCS#11 parameters describing the certificate to use. type PKCS11 struct { Slot string `json:"slot"` // The hexadecimal representation of the uint36 slot ID. (ex:0x1739427) Label string `json:"label"` // The token label (ex: gecc) @@ -54,5 +68,4 @@ func LoadConfig(configFilePath string) (config EnterpriseCertificateConfig, err return EnterpriseCertificateConfig{}, err } return config, nil - } diff --git a/internal/signer/linux/util/util_test.go b/internal/signer/util/util_test.go similarity index 65% rename from internal/signer/linux/util/util_test.go rename to internal/signer/util/util_test.go index a5b6977..7eb4263 100644 --- a/internal/signer/linux/util/util_test.go +++ b/internal/signer/util/util_test.go @@ -19,10 +19,31 @@ import ( func TestLoadConfig(t *testing.T) { config, err := LoadConfig("./test_data/certificate_config.json") + // darwin if err != nil { - t.Fatalf("LoadConfig error: %v", err) + t.Fatalf("LoadConfig error: %q", err) } - want := "0x1739427" + want := "Google Endpoint Verification" + if config.CertConfigs.MacOSKeychain.Issuer != want { + t.Errorf("Expected issuer is %q, got: %q", want, config.CertConfigs.MacOSKeychain.Issuer) + } + + // windows + want = "enterprise_v1_corp_client" + if config.CertConfigs.WindowsStore.Issuer != want { + t.Errorf("Expected issuer is %q, got: %q", want, config.CertConfigs.WindowsStore.Issuer) + } + want = "MY" + if config.CertConfigs.WindowsStore.Store != want { + t.Errorf("Expected store is %q, got: %q", want, config.CertConfigs.WindowsStore.Store) + } + want = "current_user" + if config.CertConfigs.WindowsStore.Provider != want { + t.Errorf("Expected provider is %q, got: %q", want, config.CertConfigs.WindowsStore.Provider) + } + + // pkcs11 + want = "0x1739427" if config.CertConfigs.PKCS11.Slot != want { t.Errorf("Expected slot is %v, got: %v", want, config.CertConfigs.PKCS11.Slot) } diff --git a/internal/signer/windows/go.mod b/internal/signer/windows/go.mod deleted file mode 100644 index b6b5b16..0000000 --- a/internal/signer/windows/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module signer - -go 1.19 - -require ( - golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect - golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect -) diff --git a/internal/signer/windows/go.sum b/internal/signer/windows/go.sum deleted file mode 100644 index c085ca2..0000000 --- a/internal/signer/windows/go.sum +++ /dev/null @@ -1,11 +0,0 @@ -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/internal/signer/windows/signer.go b/internal/signer/windows/signer.go index 6537ad6..32a8ee5 100644 --- a/internal/signer/windows/signer.go +++ b/internal/signer/windows/signer.go @@ -26,8 +26,9 @@ import ( "log" "net/rpc" "os" - "signer/ncrypt" - "signer/util" + + "github.com/googleapis/enterprise-certificate-proxy/internal/signer/util" + "github.com/googleapis/enterprise-certificate-proxy/internal/signer/windows/ncrypt" ) // If ECP Logging is enabled return true diff --git a/internal/signer/windows/util/test_data/certificate_config.json b/internal/signer/windows/util/test_data/certificate_config.json deleted file mode 100644 index 567f719..0000000 --- a/internal/signer/windows/util/test_data/certificate_config.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "cert_configs": { - "windows_store": { - "issuer": "enterprise_v1_corp_client", - "store": "MY", - "provider": "current_user" - } - } -} diff --git a/internal/signer/windows/util/util.go b/internal/signer/windows/util/util.go deleted file mode 100644 index a2bb1bd..0000000 --- a/internal/signer/windows/util/util.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2022 Google LLC. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package util provides helper functions for the signer. -package util - -import ( - "encoding/json" - "io" - "os" -) - -// EnterpriseCertificateConfig contains parameters for initializing signer. -type EnterpriseCertificateConfig struct { - CertConfigs CertConfigs `json:"cert_configs"` -} - -// CertConfigs is a container for various ECP Configs. -type CertConfigs struct { - WindowsStore WindowsStore `json:"windows_store"` -} - -// WindowsStore contains parameters describing the certificate to use. -type WindowsStore struct { - Issuer string `json:"issuer"` - Store string `json:"store"` - Provider string `json:"provider"` -} - -// LoadConfig retrieves the ECP config file. -func LoadConfig(configFilePath string) (config EnterpriseCertificateConfig, err error) { - jsonFile, err := os.Open(configFilePath) - if err != nil { - return EnterpriseCertificateConfig{}, err - } - - byteValue, err := io.ReadAll(jsonFile) - if err != nil { - return EnterpriseCertificateConfig{}, err - } - err = json.Unmarshal(byteValue, &config) - if err != nil { - return EnterpriseCertificateConfig{}, err - } - return config, nil - -} diff --git a/internal/signer/windows/util/util_test.go b/internal/signer/windows/util/util_test.go deleted file mode 100644 index 89ad6e6..0000000 --- a/internal/signer/windows/util/util_test.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2022 Google LLC. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package util - -import ( - "testing" -) - -func TestLoadConfig(t *testing.T) { - config, err := LoadConfig("./test_data/certificate_config.json") - if err != nil { - t.Errorf("LoadConfig error: %q", err) - } - want := "enterprise_v1_corp_client" - if config.CertConfigs.WindowsStore.Issuer != want { - t.Errorf("Expected issuer is %q, got: %q", want, config.CertConfigs.WindowsStore.Issuer) - } - want = "MY" - if config.CertConfigs.WindowsStore.Store != want { - t.Errorf("Expected store is %q, got: %q", want, config.CertConfigs.WindowsStore.Store) - } - want = "current_user" - if config.CertConfigs.WindowsStore.Provider != want { - t.Errorf("Expected provider is %q, got: %q", want, config.CertConfigs.WindowsStore.Provider) - } -} diff --git a/linux/client.go b/linux/client.go index 2a163fc..dd14ddc 100644 --- a/linux/client.go +++ b/linux/client.go @@ -12,7 +12,7 @@ // limitations under the License. // Package linux contains a linux-specific client for accessing the PKCS#11 APIs directly, -// bypassing the RPC-mechanims of the universal client. +// bypassing the RPC-mechanism of the universal client. package linux import ( diff --git a/windows/client.go b/windows/client.go new file mode 100644 index 0000000..a6b6b03 --- /dev/null +++ b/windows/client.go @@ -0,0 +1,58 @@ +// Copyright 2023 Google LLC. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package windows contains a windows-specific client for accessing the ncrypt APIs directly, +// bypassing the RPC-mechanism of the universal client. +package windows + +import ( + "crypto" + "io" + + "github.com/googleapis/enterprise-certificate-proxy/internal/signer/windows/ncrypt" +) + +// SecureKey is a public wrapper for the internal ncrypt implementation. +type SecureKey struct { + key *ncrypt.Key +} + +// CertificateChain returns the SecureKey's raw X509 cert chain. This contains the public key. +func (sk *SecureKey) CertificateChain() [][]byte { + return sk.key.CertificateChain() +} + +// Public returns the public key for this SecureKey. +func (sk *SecureKey) Public() crypto.PublicKey { + return sk.key.Public() +} + +// Sign signs a message digest, using the specified signer options. +func (sk *SecureKey) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) (signed []byte, err error) { + return sk.key.Sign(nil, digest, opts) +} + +// Close frees up resources associated with the underlying key. +func (sk *SecureKey) Close() { + sk.key.Close() +} + +// NewSecureKey returns a handle to the first available certificate and private key pair in +// the specified Windows key store matching the filters. +func NewSecureKey(issuer string, store string, provider string) (*SecureKey, error) { + k, err := ncrypt.Cred(issuer, store, provider) + if err != nil { + return nil, err + } + return &SecureKey{key: k}, nil +}