Refactor preparation of test SCEP CAs

This commit is contained in:
Herman Slatman
2024-10-01 14:04:11 +02:00
parent a0dd8d53ee
commit 69486d388f
4 changed files with 107 additions and 216 deletions

View File

@@ -2,6 +2,7 @@ package sceptest
import (
"context"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
@@ -10,6 +11,7 @@ import (
"crypto/x509/pkix"
"encoding/asn1"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
@@ -17,6 +19,7 @@ import (
"net"
"net/http"
"net/url"
"path/filepath"
"testing"
"time"
@@ -25,14 +28,19 @@ import (
"github.com/smallstep/pkcs7"
"github.com/smallstep/scep"
"go.step.sm/crypto/keyutil"
"go.step.sm/crypto/minica"
"go.step.sm/crypto/pemutil"
"go.step.sm/crypto/x509util"
"github.com/smallstep/certificates/authority/config"
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/ca"
"github.com/smallstep/certificates/cas/apiv1"
)
func newCAClient(t *testing.T, caURL, rootFilepath string) *ca.Client {
t.Helper()
caClient, err := ca.NewClient(
caURL,
ca.WithRootFile(rootFilepath),
@@ -72,6 +80,86 @@ func reservePort(t *testing.T) (host, port string) {
return
}
type testCA struct {
ca *ca.CA
caURL string
rootFilepath string
root *x509.Certificate
}
func (t *testCA) run() error {
return t.ca.Run()
}
func (t *testCA) stop() error {
return t.ca.Stop()
}
func newTestCA(t *testing.T, name string) *testCA {
t.Helper()
signer, err := keyutil.GenerateSigner("RSA", "", 2048)
require.NoError(t, err)
dir := t.TempDir()
m, err := minica.New(minica.WithName(name), minica.WithGetSignerFunc(func() (crypto.Signer, error) {
return signer, nil
}))
require.NoError(t, err)
rootFilepath := filepath.Join(dir, "root.crt")
_, err = pemutil.Serialize(m.Root, pemutil.WithFilename(rootFilepath))
require.NoError(t, err)
intermediateCertFilepath := filepath.Join(dir, "intermediate.crt")
_, err = pemutil.Serialize(m.Intermediate, pemutil.WithFilename(intermediateCertFilepath))
require.NoError(t, err)
intermediateKeyFilepath := filepath.Join(dir, "intermediate.key")
_, err = pemutil.Serialize(m.Signer, pemutil.WithFilename(intermediateKeyFilepath))
require.NoError(t, err)
// get a random address to listen on and connect to; currently no nicer way to get one before starting the server
host, port := reservePort(t)
prov := &provisioner.SCEP{
ID: "scep",
Name: "scep",
Type: "SCEP",
ForceCN: false,
ChallengePassword: "",
EncryptionAlgorithmIdentifier: 2,
MinimumPublicKeyLength: 2048,
Claims: &config.GlobalProvisionerClaims,
}
err = prov.Init(provisioner.Config{})
require.NoError(t, err)
cfg := &config.Config{
Root: []string{rootFilepath},
IntermediateCert: intermediateCertFilepath,
IntermediateKey: intermediateKeyFilepath,
Address: net.JoinHostPort(host, port), // reuse the address that was just "reserved"
DNSNames: []string{"127.0.0.1", "[::1]", "localhost"},
AuthorityConfig: &config.AuthConfig{
AuthorityID: "stepca-test-scep",
DeploymentType: "standalone-test",
Provisioners: provisioner.List{prov},
},
Logger: json.RawMessage(`{"format": "text"}`),
}
c, err := ca.New(cfg)
require.NoError(t, err)
return &testCA{
ca: c,
caURL: fmt.Sprintf("https://localhost:%s", port),
rootFilepath: rootFilepath,
root: m.Root,
}
}
type client struct {
caURL string
caCert *x509.Certificate
@@ -90,7 +178,7 @@ func createSCEPClient(t *testing.T, caURL string, root *x509.Certificate) *clien
Transport: transport,
}
return &client{
caURL: caURL,
caURL: fmt.Sprintf("%s/scep/scep", caURL),
httpClient: httpClient,
}
}
@@ -301,7 +389,7 @@ func (c *client) requestCertificateEmulatingWindowsClient(t *testing.T, commonNa
h := sha1.Sum(publicKeyBytes)
subjectKeyId := h[:]
// add subject key ID extension
// create subject key ID extension
value, err = asn1.Marshal(subjectKeyId)
if err != nil {
return nil, fmt.Errorf("failed marshaling subject key ID: %w", err)

View File

@@ -1,106 +1,40 @@
package sceptest
import (
"crypto"
"encoding/json"
"fmt"
"net"
"net/http"
"path/filepath"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.step.sm/crypto/keyutil"
"go.step.sm/crypto/minica"
"go.step.sm/crypto/pemutil"
"github.com/smallstep/certificates/authority/config"
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/ca"
)
func TestIssuesCertificateUsingRegularSCEPConfiguration(t *testing.T) {
signer, err := keyutil.GenerateSigner("RSA", "", 2048)
require.NoError(t, err)
dir := t.TempDir()
m, err := minica.New(minica.WithName("Step E2E | SCEP Regular"), minica.WithGetSignerFunc(func() (crypto.Signer, error) {
return signer, nil
}))
require.NoError(t, err)
rootFilepath := filepath.Join(dir, "root.crt")
_, err = pemutil.Serialize(m.Root, pemutil.WithFilename(rootFilepath))
require.NoError(t, err)
intermediateCertFilepath := filepath.Join(dir, "intermediate.crt")
_, err = pemutil.Serialize(m.Intermediate, pemutil.WithFilename(intermediateCertFilepath))
require.NoError(t, err)
intermediateKeyFilepath := filepath.Join(dir, "intermediate.key")
_, err = pemutil.Serialize(m.Signer, pemutil.WithFilename(intermediateKeyFilepath))
require.NoError(t, err)
// get a random address to listen on and connect to; currently no nicer way to get one before starting the server
// TODO(hs): find/implement a nicer way to expose the CA URL, similar to how e.g. httptest.Server exposes it?
host, port := reservePort(t)
prov := &provisioner.SCEP{
ID: "scep",
Name: "scep",
Type: "SCEP",
ForceCN: false,
ChallengePassword: "",
EncryptionAlgorithmIdentifier: 2,
MinimumPublicKeyLength: 2048,
Claims: &config.GlobalProvisionerClaims,
}
err = prov.Init(provisioner.Config{})
require.NoError(t, err)
cfg := &config.Config{
Root: []string{rootFilepath},
IntermediateCert: intermediateCertFilepath,
IntermediateKey: intermediateKeyFilepath,
Address: net.JoinHostPort(host, port), // reuse the address that was just "reserved"
DNSNames: []string{"127.0.0.1", "[::1]", "localhost"},
AuthorityConfig: &config.AuthConfig{
AuthorityID: "stepca-test-scep",
DeploymentType: "standalone-test",
Provisioners: provisioner.List{prov},
},
Logger: json.RawMessage(`{"format": "text"}`),
}
c, err := ca.New(cfg)
require.NoError(t, err)
c := newTestCA(t, "Step E2E | SCEP Regular")
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
err = c.Run()
err := c.run()
require.ErrorIs(t, err, http.ErrServerClosed)
}()
// instantiate a client for the CA running at the random address
caClient := newCAClient(t, fmt.Sprintf("https://localhost:%s", port), rootFilepath)
caClient := newCAClient(t, c.caURL, c.rootFilepath)
requireHealthyCA(t, caClient)
scepClient := createSCEPClient(t, fmt.Sprintf("https://localhost:%s/scep/scep", port), m.Root)
scepClient := createSCEPClient(t, c.caURL, c.root)
cert, err := scepClient.requestCertificate(t, "test.localhost", []string{"test.localhost"})
assert.NoError(t, err)
require.NoError(t, err)
require.NotNil(t, cert)
assert.Equal(t, "test.localhost", cert.Subject.CommonName)
assert.Equal(t, "Step E2E | SCEP Regular Intermediate CA", cert.Issuer.CommonName)
// done testing; stop and wait for the server to quit
err = c.Stop()
err = c.stop()
require.NoError(t, err)
wg.Wait()

View File

@@ -3,26 +3,15 @@
package sceptest
import (
"crypto"
"crypto/x509"
"encoding/json"
"fmt"
"net"
"net/http"
"path/filepath"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.step.sm/crypto/keyutil"
"go.step.sm/crypto/minica"
"go.step.sm/crypto/pemutil"
"github.com/smallstep/certificates/authority/config"
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/ca"
legacyx509 "github.com/smallstep/certificates/test/integration/scep/internal/x509"
)
@@ -36,77 +25,24 @@ func legacyCertificateParser(der []byte) (*x509.Certificate, error) {
}
func TestIssuesCertificateToEmulatedWindowsClientGo123(t *testing.T) {
signer, err := keyutil.GenerateSigner("RSA", "", 2048)
require.NoError(t, err)
dir := t.TempDir()
m, err := minica.New(minica.WithName("Step E2E | SCEP Regular w/ Windows Client"), minica.WithGetSignerFunc(func() (crypto.Signer, error) {
return signer, nil
}))
require.NoError(t, err)
rootFilepath := filepath.Join(dir, "root.crt")
_, err = pemutil.Serialize(m.Root, pemutil.WithFilename(rootFilepath))
require.NoError(t, err)
intermediateCertFilepath := filepath.Join(dir, "intermediate.crt")
_, err = pemutil.Serialize(m.Intermediate, pemutil.WithFilename(intermediateCertFilepath))
require.NoError(t, err)
intermediateKeyFilepath := filepath.Join(dir, "intermediate.key")
_, err = pemutil.Serialize(m.Signer, pemutil.WithFilename(intermediateKeyFilepath))
require.NoError(t, err)
// get a random address to listen on and connect to; currently no nicer way to get one before starting the server
// TODO(hs): find/implement a nicer way to expose the CA URL, similar to how e.g. httptest.Server exposes it?
host, port := reservePort(t)
prov := &provisioner.SCEP{
ID: "scep",
Name: "scep",
Type: "SCEP",
ForceCN: false,
ChallengePassword: "",
EncryptionAlgorithmIdentifier: 2,
MinimumPublicKeyLength: 2048,
Claims: &config.GlobalProvisionerClaims,
}
err = prov.Init(provisioner.Config{})
require.NoError(t, err)
cfg := &config.Config{
Root: []string{rootFilepath},
IntermediateCert: intermediateCertFilepath,
IntermediateKey: intermediateKeyFilepath,
Address: net.JoinHostPort(host, port), // reuse the address that was just "reserved"
DNSNames: []string{"127.0.0.1", "[::1]", "localhost"},
AuthorityConfig: &config.AuthConfig{
AuthorityID: "stepca-test-scep",
DeploymentType: "standalone-test",
Provisioners: provisioner.List{prov},
},
Logger: json.RawMessage(`{"format": "text"}`),
}
c, err := ca.New(cfg)
require.NoError(t, err)
c := newTestCA(t, "Step E2E | SCEP Regular w/ Windows Client")
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
err = c.Run()
err := c.Run()
require.ErrorIs(t, err, http.ErrServerClosed)
}()
// instantiate a client for the CA running at the random address
caClient := newCAClient(t, fmt.Sprintf("https://localhost:%s", port), rootFilepath)
caClient := newCAClient(t, c.caURL, c.rootFilepath)
requireHealthyCA(t, caClient)
scepClient := createSCEPClient(t, fmt.Sprintf("https://localhost:%s/scep/scep", port), m.Root)
scepClient := createSCEPClient(t, c.caURL, c.root)
cert, err := scepClient.requestCertificateEmulatingWindowsClient(t, "test.localhost", []string{"test.localhost"}, legacyCertificateParser)
assert.NoError(t, err)
require.NoError(t, err)
require.NotNil(t, cert)
assert.Equal(t, "test.localhost", cert.Subject.CommonName)

View File

@@ -3,107 +3,40 @@
package sceptest
import (
"crypto"
"crypto/x509"
"encoding/json"
"fmt"
"net"
"net/http"
"path/filepath"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.step.sm/crypto/keyutil"
"go.step.sm/crypto/minica"
"go.step.sm/crypto/pemutil"
"github.com/smallstep/certificates/authority/config"
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/ca"
)
func TestIssuesCertificateToEmulatedWindowsClient(t *testing.T) {
signer, err := keyutil.GenerateSigner("RSA", "", 2048)
require.NoError(t, err)
dir := t.TempDir()
m, err := minica.New(minica.WithName("Step E2E | SCEP Regular w/ Windows Client"), minica.WithGetSignerFunc(func() (crypto.Signer, error) {
return signer, nil
}))
require.NoError(t, err)
rootFilepath := filepath.Join(dir, "root.crt")
_, err = pemutil.Serialize(m.Root, pemutil.WithFilename(rootFilepath))
require.NoError(t, err)
intermediateCertFilepath := filepath.Join(dir, "intermediate.crt")
_, err = pemutil.Serialize(m.Intermediate, pemutil.WithFilename(intermediateCertFilepath))
require.NoError(t, err)
intermediateKeyFilepath := filepath.Join(dir, "intermediate.key")
_, err = pemutil.Serialize(m.Signer, pemutil.WithFilename(intermediateKeyFilepath))
require.NoError(t, err)
// get a random address to listen on and connect to; currently no nicer way to get one before starting the server
// TODO(hs): find/implement a nicer way to expose the CA URL, similar to how e.g. httptest.Server exposes it?
host, port := reservePort(t)
prov := &provisioner.SCEP{
ID: "scep",
Name: "scep",
Type: "SCEP",
ForceCN: false,
ChallengePassword: "",
EncryptionAlgorithmIdentifier: 2,
MinimumPublicKeyLength: 2048,
Claims: &config.GlobalProvisionerClaims,
}
err = prov.Init(provisioner.Config{})
require.NoError(t, err)
cfg := &config.Config{
Root: []string{rootFilepath},
IntermediateCert: intermediateCertFilepath,
IntermediateKey: intermediateKeyFilepath,
Address: net.JoinHostPort(host, port), // reuse the address that was just "reserved"
DNSNames: []string{"127.0.0.1", "[::1]", "localhost"},
AuthorityConfig: &config.AuthConfig{
AuthorityID: "stepca-test-scep",
DeploymentType: "standalone-test",
Provisioners: provisioner.List{prov},
},
Logger: json.RawMessage(`{"format": "text"}`),
}
c, err := ca.New(cfg)
require.NoError(t, err)
c := newTestCA(t, "Step E2E | SCEP Regular w/ Windows Client")
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
err = c.Run()
err := c.run()
require.ErrorIs(t, err, http.ErrServerClosed)
}()
// instantiate a client for the CA running at the random address
caClient := newCAClient(t, fmt.Sprintf("https://localhost:%s", port), rootFilepath)
caClient := newCAClient(t, c.caURL, c.rootFilepath)
requireHealthyCA(t, caClient)
scepClient := createSCEPClient(t, fmt.Sprintf("https://localhost:%s/scep/scep", port), m.Root)
scepClient := createSCEPClient(t, c.caURL, c.root)
cert, err := scepClient.requestCertificateEmulatingWindowsClient(t, "test.localhost", []string{"test.localhost"}, x509.ParseCertificate)
assert.NoError(t, err)
require.NoError(t, err)
require.NotNil(t, cert)
assert.Equal(t, "test.localhost", cert.Subject.CommonName)
assert.Equal(t, "Step E2E | SCEP Regular w/ Windows Client Intermediate CA", cert.Issuer.CommonName)
// done testing; stop and wait for the server to quit
err = c.Stop()
err = c.stop()
require.NoError(t, err)
wg.Wait()