| package pki |
| |
| import ( |
| "bytes" |
| "context" |
| "crypto/ed25519" |
| "crypto/rand" |
| "crypto/x509" |
| "testing" |
| |
| "go.etcd.io/etcd/integration" |
| ) |
| |
| // TestManaged ensures Managed Certificates work, including re-ensuring |
| // certificates with the same data and issuing subordinate certificates. |
| func TestManaged(t *testing.T) { |
| cluster := integration.NewClusterV3(nil, &integration.ClusterConfig{ |
| Size: 1, |
| }) |
| cl := cluster.Client(0) |
| defer cluster.Terminate(nil) |
| ctx, ctxC := context.WithCancel(context.Background()) |
| defer ctxC() |
| ns := Namespaced("/test-managed/") |
| |
| // Test CA certificate issuance. |
| ca := &Certificate{ |
| Namespace: &ns, |
| Issuer: SelfSigned, |
| Name: "ca", |
| Template: CA("Test CA"), |
| } |
| caBytes, err := ca.Ensure(ctx, cl) |
| if err != nil { |
| t.Fatalf("Failed to Ensure CA: %v", err) |
| } |
| caCert, err := x509.ParseCertificate(caBytes) |
| if err != nil { |
| t.Fatalf("Failed to parse newly emited CA cert: %v", err) |
| } |
| if !caCert.IsCA { |
| t.Errorf("Newly emitted CA cert is not CA") |
| } |
| if ca.PublicKey == nil { |
| t.Errorf("Newly emitted CA cert has no public key") |
| } |
| if ca.PrivateKey == nil { |
| t.Errorf("Newly emitted CA cert has no public key") |
| } |
| |
| // Re-emitting CA certificate with same parameters should return exact same |
| // data. |
| ca2 := &Certificate{ |
| Namespace: &ns, |
| Issuer: SelfSigned, |
| Name: "ca", |
| Template: CA("Test CA"), |
| } |
| caBytes2, err := ca2.Ensure(ctx, cl) |
| if err != nil { |
| t.Fatalf("Failed to re-Ensure CA: %v", err) |
| } |
| if !bytes.Equal(caBytes, caBytes2) { |
| t.Errorf("New CA has different x509 certificate") |
| } |
| if !bytes.Equal(ca.PublicKey, ca2.PublicKey) { |
| t.Errorf("New CA has different public key") |
| } |
| if !bytes.Equal(ca.PrivateKey, ca2.PrivateKey) { |
| t.Errorf("New CA has different private key") |
| } |
| |
| // Emitting a subordinate certificate should work. |
| client := &Certificate{ |
| Namespace: &ns, |
| Issuer: ca2, |
| Name: "client", |
| Template: Client("foo", nil), |
| } |
| clientBytes, err := client.Ensure(ctx, cl) |
| if err != nil { |
| t.Fatalf("Failed to ensure client certificate: %v", err) |
| } |
| clientCert, err := x509.ParseCertificate(clientBytes) |
| if err != nil { |
| t.Fatalf("Failed to parse newly emitted client certificate: %v", err) |
| } |
| if clientCert.IsCA { |
| t.Errorf("New client cert is CA") |
| } |
| if want, got := "foo", clientCert.Subject.CommonName; want != got { |
| t.Errorf("New client CN should be %q, got %q", want, got) |
| } |
| if want, got := caCert.Subject.String(), clientCert.Issuer.String(); want != got { |
| t.Errorf("New client issuer should be %q, got %q", want, got) |
| } |
| } |
| |
| // TestExternal ensures External certificates work correctly, including |
| // re-Ensuring certificates with the same public key, and attempting to re-issue |
| // the same certificate with a different public key (which should fail). |
| func TestExternal(t *testing.T) { |
| cluster := integration.NewClusterV3(nil, &integration.ClusterConfig{ |
| Size: 1, |
| }) |
| cl := cluster.Client(0) |
| defer cluster.Terminate(nil) |
| ctx, ctxC := context.WithCancel(context.Background()) |
| defer ctxC() |
| ns := Namespaced("/test-external/") |
| |
| ca := &Certificate{ |
| Namespace: &ns, |
| Issuer: SelfSigned, |
| Name: "ca", |
| Template: CA("Test CA"), |
| } |
| |
| // Issuing an external certificate should work. |
| pk, _, err := ed25519.GenerateKey(rand.Reader) |
| if err != nil { |
| t.Fatalf("GenerateKey: %v", err) |
| } |
| server := &Certificate{ |
| Namespace: &ns, |
| Issuer: ca, |
| Name: "server", |
| Template: Server([]string{"server"}, nil), |
| Mode: CertificateExternal, |
| PublicKey: pk, |
| } |
| serverBytes, err := server.Ensure(ctx, cl) |
| if err != nil { |
| t.Fatalf("Failed to Ensure server certificate: %v", err) |
| } |
| |
| // Issuing an external certificate with the same name but different public key |
| // should fail. |
| pk2, _, err := ed25519.GenerateKey(rand.Reader) |
| if err != nil { |
| t.Fatalf("GenerateKey: %v", err) |
| } |
| server2 := &Certificate{ |
| Namespace: &ns, |
| Issuer: ca, |
| Name: "server", |
| Template: Server([]string{"server"}, nil), |
| Mode: CertificateExternal, |
| PublicKey: pk2, |
| } |
| if _, err := server2.Ensure(ctx, cl); err == nil { |
| t.Fatalf("Issuing server certificate with different public key should have failed") |
| } |
| |
| // Issuing the external certificate with the same name and same public key |
| // should work and yield the same x509 bytes. |
| server3 := &Certificate{ |
| Namespace: &ns, |
| Issuer: ca, |
| Name: "server", |
| Template: Server([]string{"server"}, nil), |
| Mode: CertificateExternal, |
| PublicKey: pk, |
| } |
| serverBytes3, err := server3.Ensure(ctx, cl) |
| if err != nil { |
| t.Fatalf("Failed to re-Ensure server certificate: %v", err) |
| } |
| if !bytes.Equal(serverBytes, serverBytes3) { |
| t.Errorf("New server certificate has different x509 certificate") |
| } |
| } |