2024-04-22 14:56:55 +00:00
|
|
|
package sceptest
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto"
|
|
|
|
"crypto/x509"
|
|
|
|
"crypto/x509/pkix"
|
|
|
|
"encoding/json"
|
|
|
|
"encoding/pem"
|
|
|
|
"fmt"
|
|
|
|
"math/big"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"path/filepath"
|
|
|
|
"sync"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"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"
|
|
|
|
"github.com/smallstep/certificates/cas/apiv1"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestIssuesCertificateUsingSCEPWithDecrypterAndUpstreamCAS(t *testing.T) {
|
|
|
|
signer, err := keyutil.GenerateSigner("EC", "P-256", 0)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
m, err := minica.New(minica.WithName("Step E2E | SCEP Decrypter w/ Upstream CAS"), 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)
|
|
|
|
|
|
|
|
decrypterKey, err := keyutil.GenerateKey("RSA", "", 2048)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
decrypter, ok := decrypterKey.(crypto.Decrypter)
|
|
|
|
require.True(t, ok)
|
|
|
|
|
|
|
|
decrypterCertifiate, err := m.Sign(&x509.Certificate{
|
|
|
|
Subject: pkix.Name{CommonName: "decrypter"},
|
|
|
|
PublicKey: decrypter.Public(),
|
|
|
|
SerialNumber: big.NewInt(1),
|
|
|
|
NotBefore: time.Now().Add(-1 * time.Hour),
|
|
|
|
NotAfter: time.Now().Add(1 * time.Hour),
|
|
|
|
DNSNames: []string{"decrypter"},
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
b, err := pemutil.Serialize(decrypterCertifiate)
|
|
|
|
require.NoError(t, err)
|
|
|
|
decrypterCertificatePEMBytes := pem.EncodeToMemory(b)
|
|
|
|
|
|
|
|
b, err = pemutil.Serialize(decrypter, pemutil.WithPassword([]byte("1234")))
|
|
|
|
require.NoError(t, err)
|
|
|
|
decrypterKeyPEMBytes := pem.EncodeToMemory(b)
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
DecrypterCertificate: decrypterCertificatePEMBytes,
|
|
|
|
DecrypterKeyPEM: decrypterKeyPEMBytes,
|
|
|
|
DecrypterKeyPassword: "1234",
|
|
|
|
}
|
|
|
|
|
|
|
|
err = prov.Init(provisioner.Config{})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
apiv1.Register("test-scep-cas", func(_ context.Context, opts apiv1.Options) (apiv1.CertificateAuthorityService, error) {
|
|
|
|
return &testCAS{
|
|
|
|
ca: m,
|
|
|
|
}, nil
|
|
|
|
})
|
|
|
|
|
|
|
|
cfg := &config.Config{
|
2024-04-22 15:14:54 +00:00
|
|
|
Address: net.JoinHostPort(host, port), // reuse the address that was just "reserved"
|
|
|
|
DNSNames: []string{"127.0.0.1", "[::1]", "localhost"},
|
2024-04-22 14:56:55 +00:00
|
|
|
AuthorityConfig: &config.AuthConfig{
|
|
|
|
Options: &apiv1.Options{
|
|
|
|
AuthorityID: "stepca-test-scep",
|
|
|
|
Type: "test-scep-cas",
|
|
|
|
CertificateAuthority: "test-cas",
|
|
|
|
},
|
|
|
|
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)
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(1)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
err = c.Run()
|
|
|
|
require.ErrorIs(t, err, http.ErrServerClosed)
|
|
|
|
}()
|
|
|
|
|
2024-04-22 17:12:54 +00:00
|
|
|
// instantiate a client for the CA running at the random address
|
|
|
|
caClient := newCAClient(t, fmt.Sprintf("https://localhost:%s", port), rootFilepath)
|
|
|
|
requireHealthyCA(t, caClient)
|
2024-04-22 14:56:55 +00:00
|
|
|
|
2024-04-22 17:12:54 +00:00
|
|
|
scepClient := createSCEPClient(t, fmt.Sprintf("https://localhost:%s/scep/scep", port), m.Root)
|
2024-04-22 14:56:55 +00:00
|
|
|
cert, err := scepClient.requestCertificate(t, "test.localhost", []string{"test.localhost"})
|
|
|
|
assert.NoError(t, err)
|
|
|
|
require.NotNil(t, cert)
|
|
|
|
|
|
|
|
assert.Equal(t, "test.localhost", cert.Subject.CommonName)
|
|
|
|
assert.Equal(t, "Step E2E | SCEP Decrypter w/ Upstream CAS Intermediate CA", cert.Issuer.CommonName)
|
|
|
|
|
|
|
|
// done testing; stop and wait for the server to quit
|
|
|
|
err = c.Stop()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
}
|