Skip to content

Commit

Permalink
Add test case emulating Windows SCEP client
Browse files Browse the repository at this point in the history
  • Loading branch information
hslatman committed Oct 1, 2024
1 parent 35a4d6e commit a0dd8d5
Show file tree
Hide file tree
Showing 12 changed files with 2,519 additions and 19 deletions.
7 changes: 2 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ require (
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935
github.com/smallstep/nosql v0.7.0
github.com/smallstep/pkcs7 v0.0.0-20240911091500-b1cae6277023
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d
github.com/smallstep/scep v0.0.0-20240926084937-8cf1ca453101
github.com/stretchr/testify v1.9.0
github.com/urfave/cli v1.22.15
go.step.sm/cli-utils v0.9.0
Expand Down Expand Up @@ -92,9 +92,6 @@ require (
github.com/fatih/color v1.16.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
github.com/go-kit/kit v0.13.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-piv/piv-go v1.11.0 // indirect
Expand All @@ -105,7 +102,7 @@ require (
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/certificate-transparency-go v1.1.6 // indirect
github.com/google/certificate-transparency-go v1.1.7 // indirect
github.com/google/go-tpm-tools v0.4.4 // indirect
github.com/google/go-tspi v0.3.0 // indirect
github.com/google/s2a-go v0.1.8 // indirect
Expand Down
18 changes: 4 additions & 14 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,6 @@ github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU=
github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg=
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
Expand All @@ -165,7 +157,6 @@ github.com/go-piv/piv-go v1.11.0/go.mod h1:NZ2zmjVkfFaL/CF8cVQ/pXdXtuj110zEKGdJM
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
Expand Down Expand Up @@ -198,8 +189,8 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
github.com/google/certificate-transparency-go v1.1.6 h1:SW5K3sr7ptST/pIvNkSVWMiJqemRmkjJPPT0jzXdOOY=
github.com/google/certificate-transparency-go v1.1.6/go.mod h1:0OJjOsOk+wj6aYQgP7FU0ioQ0AJUmnWPFMqTjQeazPQ=
github.com/google/certificate-transparency-go v1.1.7 h1:IASD+NtgSTJLPdzkthwvAG1ZVbF2WtFg4IvoA68XGSw=
github.com/google/certificate-transparency-go v1.1.7/go.mod h1:FSSBo8fyMVgqptbfF6j5p/XNdgQftAhSmXcIxV9iphE=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
Expand Down Expand Up @@ -386,11 +377,10 @@ github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 h1:kjYv
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4=
github.com/smallstep/nosql v0.7.0 h1:YiWC9ZAHcrLCrayfaF+QJUv16I2bZ7KdLC3RpJcnAnE=
github.com/smallstep/nosql v0.7.0/go.mod h1:H5VnKMCbeq9QA6SRY5iqPylfxLfYcLwvUff3onQ8+HU=
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81/go.mod h1:SoUAr/4M46rZ3WaLstHxGhLEgoYIDRqxQEXLOmOEB0Y=
github.com/smallstep/pkcs7 v0.0.0-20240911091500-b1cae6277023 h1:klMnoL/Mrw9MJaAZdGUuEAKSskSoy14KIUpRwGOd4Vo=
github.com/smallstep/pkcs7 v0.0.0-20240911091500-b1cae6277023/go.mod h1:CM5KrX7rxWgwDdMj9yef/pJB2OPgy/56z4IEx2UIbpc=
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d h1:06LUHn4Ia2X6syjIaCMNaXXDNdU+1N/oOHynJbWgpXw=
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d/go.mod h1:4d0ub42ut1mMtvGyMensjuHYEUpRrASvkzLEJvoRQcU=
github.com/smallstep/scep v0.0.0-20240926084937-8cf1ca453101 h1:LyZqn24/ZiVg8v9Hq07K6mx6RqPtpDeK+De5vf4QEY4=
github.com/smallstep/scep v0.0.0-20240926084937-8cf1ca453101/go.mod h1:EuKQjYGQwhUa1mgD21zxIgOgUYLsqikJmvxNscxpS/Y=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
Expand Down
153 changes: 153 additions & 0 deletions test/integration/scep/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/base64"
"errors"
"fmt"
Expand Down Expand Up @@ -243,6 +246,156 @@ func (c *client) requestCertificate(t *testing.T, commonName string, sans []stri
return cert, nil
}

var (
oidExtensionAuthorityKeyId = asn1.ObjectIdentifier{2, 5, 29, 35}
oidExtensionSubjectKeyId = asn1.ObjectIdentifier{2, 5, 29, 14}
)

type authorityKeyId struct {
Id []byte `asn1:"optional,tag:0"`
}

type pkcs1PublicKey struct {
N *big.Int
E int
}

type parseFunc = func(der []byte) (*x509.Certificate, error)

func (c *client) requestCertificateEmulatingWindowsClient(t *testing.T, commonName string, sans []string, parseCertificate parseFunc) (*x509.Certificate, error) {
if err := c.getCACert(t); err != nil {
return nil, fmt.Errorf("failed getting CA certificate: %w", err)
}

signer, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, fmt.Errorf("failed creating SCEP private key: %w", err)
}

csr, err := x509util.CreateCertificateRequest(commonName, sans, signer)
if err != nil {
return nil, fmt.Errorf("failed creating CSR: %w", err)
}

// on Windows the self-signed certificate contains an authority key identifier
// extension that is marked critical
value, err := asn1.Marshal(authorityKeyId{[]byte("bla")}) // fake value
if err != nil {
return nil, fmt.Errorf("failed marshaling authority key ID")
}
authorityKeyIDExtension := pkix.Extension{
Id: oidExtensionAuthorityKeyId,
Critical: true,
Value: value,
}

// determine the subject key ID
publicKeyBytes, err := asn1.Marshal(pkcs1PublicKey{
N: signer.N,
E: signer.E,
})
if err != nil {
return nil, fmt.Errorf("failed marshaling RSA public key: %w", err)
}

h := sha1.Sum(publicKeyBytes)
subjectKeyId := h[:]

// add subject key ID extension
value, err = asn1.Marshal(subjectKeyId)
if err != nil {
return nil, fmt.Errorf("failed marshaling subject key ID: %w", err)
}
subjectKeyIDExtension := pkix.Extension{
Id: oidExtensionSubjectKeyId,
Value: value,
}

tmpl := &x509.Certificate{
Subject: pkix.Name{CommonName: "SCEP Protocol Certificate"},
SignatureAlgorithm: x509.SHA1WithRSA,
PublicKey: signer.Public(),
SerialNumber: big.NewInt(1),
NotBefore: time.Now().Add(-1 * time.Hour),
NotAfter: time.Now().Add(365 * 24 * time.Hour),
ExtraExtensions: []pkix.Extension{authorityKeyIDExtension, subjectKeyIDExtension},
}

selfSignedDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, signer.Public(), signer)
if err != nil {
return nil, fmt.Errorf("failed creating self signed certificate: %w", err)
}
selfSignedCertificate, err := parseCertificate(selfSignedDER)
if err != nil {
return nil, fmt.Errorf("failed parsing self signed certificate: %w", err)
}

msgTmpl := &scep.PKIMessage{
TransactionID: "test-1",
MessageType: scep.PKCSReq,
SenderNonce: []byte("test-nonce-1"),
Recipients: []*x509.Certificate{c.caCert},
SignerCert: selfSignedCertificate,
SignerKey: signer,
}

msg, err := scep.NewCSRRequest(csr, msgTmpl)
if err != nil {
return nil, fmt.Errorf("failed creating SCEP PKCSReq message: %w", err)
}

t.Log(string(msg.Raw))

u, err := url.Parse(c.caURL)
if err != nil {
return nil, fmt.Errorf("failed parsing CA URL: %w", err)
}

opURL := u.ResolveReference(&url.URL{RawQuery: fmt.Sprintf("operation=PKIOperation&message=%s", url.QueryEscape(base64.StdEncoding.EncodeToString(msg.Raw)))})
resp, err := c.httpClient.Get(opURL.String())
if err != nil {
return nil, fmt.Errorf("failed get request: %w", err)
}
defer resp.Body.Close()

if ct := resp.Header.Get("Content-Type"); ct != "application/x-pki-message" {
return nil, fmt.Errorf("received unexpected content type %q", ct)
}

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed reading response body: %w", err)
}

t.Log(string(body))

signedData, err := pkcs7.Parse(body)
if err != nil {
return nil, fmt.Errorf("failed parsing response body: %w", err)
}

// TODO: verify the signature?

p7, err := pkcs7.Parse(signedData.Content)
if err != nil {
return nil, fmt.Errorf("failed decrypting inner p7: %w", err)
}

content, err := p7.Decrypt(selfSignedCertificate, signer)
if err != nil {
return nil, fmt.Errorf("failed decrypting response: %w", err)
}

p7, err = pkcs7.Parse(content)
if err != nil {
return nil, fmt.Errorf("failed parsing p7 content: %w", err)
}

cert := p7.Certificates[0]

return cert, nil
}

type testCAS struct {
ca *minica.CA
}
Expand Down
14 changes: 14 additions & 0 deletions test/integration/scep/internal/x509/debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package legacyx509

import "fmt"

// legacyGodebugSetting is a type mimicking Go's internal godebug package
// settings, which are used to enable / disable certain functionalities at
// build time.
type legacyGodebugSetting int

func (s legacyGodebugSetting) Value() string {
return fmt.Sprintf("%d", s)
}

func (s legacyGodebugSetting) IncNonDefault() {}
16 changes: 16 additions & 0 deletions test/integration/scep/internal/x509/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
Package legacyx509 is a copy of certain parts of Go's crypto/x509 package.
It is based on Go 1.23, and has just the parts copied over required for
parsing X509 certificates.
The copy in this repository is intended to be used for preparing a SCEP request
emulating a Windows SCEP client. The Windows SCEP client markes the authority
key identifier as critical in the self-signed SCEP enrolment certificate, which
fails to parse using the standard X509 parser in Go 1.23 and later.
This is itself a copy from the copy in our PKCS7 package. We currently don't
intend to maintain that in an importable package, since we only need these copies
for testing purposes, hence needing another copy of the code.
*/

package legacyx509
Loading

0 comments on commit a0dd8d5

Please sign in to comment.