Skip to content

Commit bb7814e

Browse files
committed
test: Correct the server and client certificates used in dialer_test.go
1 parent fef405b commit bb7814e

File tree

4 files changed

+383
-147
lines changed

4 files changed

+383
-147
lines changed

internal/cloudsql/instance_test.go

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -150,19 +150,6 @@ func TestConnectionInfoTLSConfig(t *testing.T) {
150150
t.Fatal(err)
151151
}
152152

153-
// Now self sign the server's cert
154-
// TODO: this also should return structured data and handle the PEM
155-
// encoding elsewhere
156-
certBytes, err := mock.SelfSign(i.Cert, i.Key)
157-
if err != nil {
158-
t.Fatal(err)
159-
}
160-
b, _ = pem.Decode(certBytes)
161-
serverCACert, err := x509.ParseCertificate(b.Bytes)
162-
if err != nil {
163-
t.Fatal(err)
164-
}
165-
166153
// Assemble a connection info with the raw and parsed client cert
167154
// and the self-signed server certificate
168155
ci := ConnectionInfo{
@@ -172,7 +159,7 @@ func TestConnectionInfoTLSConfig(t *testing.T) {
172159
PrivateKey: RSAKey,
173160
Leaf: clientCert,
174161
},
175-
ServerCACert: []*x509.Certificate{serverCACert},
162+
ServerCACert: []*x509.Certificate{i.Cert},
176163
DBVersion: "doesn't matter here",
177164
Expiration: clientCert.NotAfter,
178165
}
@@ -198,7 +185,7 @@ func TestConnectionInfoTLSConfig(t *testing.T) {
198185
}
199186

200187
verifyPeerCert := got.VerifyPeerCertificate
201-
err = verifyPeerCert([][]byte{serverCACert.Raw}, nil)
188+
err = verifyPeerCert([][]byte{i.Cert.Raw}, nil)
202189
if err != nil {
203190
t.Fatalf("expected to verify peer cert, got error: %v", err)
204191
}

internal/mock/certs.go

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
// Copyright 2025 Google LLC
2+
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package mock
16+
17+
import (
18+
"bytes"
19+
"crypto/rand"
20+
"crypto/rsa"
21+
"crypto/sha1"
22+
"crypto/tls"
23+
"crypto/x509"
24+
"crypto/x509/pkix"
25+
"encoding/binary"
26+
"encoding/pem"
27+
"math/big"
28+
"time"
29+
)
30+
31+
func name(cn string) pkix.Name {
32+
return pkix.Name{
33+
Country: []string{"US"},
34+
Organization: []string{"Google\\, Inc"},
35+
CommonName: cn,
36+
}
37+
}
38+
39+
// "C=US,O=Google\\, Inc,CN=Google Cloud SQL Root CA"
40+
var serverCaSubject = name("Google Cloud SQL Root CA")
41+
var intermediateCaSubject = name("Google Cloud SQL Intermediate CA")
42+
var signingCaSubject = name("Google Cloud SQL Signing CA foo:baz")
43+
var instanceWithCnSubject = name("myProject:myInstance")
44+
45+
// TLSCertificates generates an accurate reproduction of the TLS certificates
46+
// used by Cloud SQL. This was translated to Go from the Java connector.
47+
//
48+
// From the cloud-sql-jdbc-socket-factory project:
49+
// core/src/test/java/com/google/cloud/sql/core/TestCertificateGenerator.java
50+
type TLSCertificates struct {
51+
clientCertExpires time.Time
52+
projectName string
53+
instanceName string
54+
sans []string
55+
56+
serverCaKey *rsa.PrivateKey
57+
serverIntermediateCaKey *rsa.PrivateKey
58+
clientSigningCaKey *rsa.PrivateKey
59+
60+
serverCaCert *x509.Certificate
61+
serverIntermediateCaCert *x509.Certificate
62+
clientSigningCACertificate *x509.Certificate
63+
64+
serverKey *rsa.PrivateKey
65+
serverCert *x509.Certificate
66+
casServerCertificate *x509.Certificate
67+
}
68+
69+
func mustGenerateKey() *rsa.PrivateKey {
70+
key, err := rsa.GenerateKey(rand.Reader, 2048)
71+
if err != nil {
72+
panic(err)
73+
}
74+
return key
75+
}
76+
77+
// newTLSCertificates creates a new instance of the TLSCertificates.
78+
func newTLSCertificates(projectName, instanceName string, sans []string, clientCertExpires time.Time) *TLSCertificates {
79+
c := &TLSCertificates{
80+
clientCertExpires: clientCertExpires,
81+
projectName: projectName,
82+
instanceName: instanceName,
83+
sans: sans,
84+
}
85+
c.rotateCA()
86+
return c
87+
}
88+
89+
// generateSKI Generate public key id. Certificates need to include
90+
// the key id to make the certificate chain work.
91+
func generateSKI(pub *rsa.PublicKey) []byte {
92+
bs := make([]byte, 8)
93+
binary.LittleEndian.PutUint64(bs, uint64(pub.E))
94+
95+
hasher := sha1.New()
96+
hasher.Write(bs)
97+
if pub.N != nil {
98+
hasher.Write(pub.N.Bytes())
99+
}
100+
ski := hasher.Sum(nil)
101+
102+
return ski
103+
}
104+
105+
// mustBuildRootCertificate produces a self-signed certificate.
106+
// or panics - use only for testing.
107+
func mustBuildRootCertificate(subject pkix.Name, k *rsa.PrivateKey) *x509.Certificate {
108+
109+
sn, err := rand.Int(rand.Reader, big.NewInt(1000))
110+
if err != nil {
111+
panic(err)
112+
}
113+
114+
cert := &x509.Certificate{
115+
SerialNumber: sn,
116+
SubjectKeyId: generateSKI(&k.PublicKey),
117+
Subject: subject,
118+
NotBefore: time.Now(),
119+
NotAfter: time.Now().AddDate(1, 0, 0),
120+
IsCA: true,
121+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
122+
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
123+
BasicConstraintsValid: true,
124+
}
125+
126+
certDerBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, &k.PublicKey, k)
127+
c, err := x509.ParseCertificate(certDerBytes)
128+
if err != nil {
129+
panic(err)
130+
}
131+
return c
132+
}
133+
134+
// mustBuildSignedCertificate produces a certificate for Subject that is signed
135+
// by the issuer.
136+
func mustBuildSignedCertificate(
137+
isCa bool,
138+
subject pkix.Name,
139+
subjectPublicKey *rsa.PrivateKey,
140+
certificateIssuer pkix.Name,
141+
issuerPrivateKey *rsa.PrivateKey,
142+
notAfter time.Time,
143+
subjectAlternativeNames []string) *x509.Certificate {
144+
145+
sn, err := rand.Int(rand.Reader, big.NewInt(1000))
146+
if err != nil {
147+
panic(err)
148+
}
149+
150+
cert := &x509.Certificate{
151+
SerialNumber: sn,
152+
Subject: subject,
153+
SubjectKeyId: generateSKI(&subjectPublicKey.PublicKey),
154+
AuthorityKeyId: generateSKI(&issuerPrivateKey.PublicKey),
155+
Issuer: certificateIssuer,
156+
NotBefore: time.Now(),
157+
NotAfter: notAfter,
158+
IsCA: isCa,
159+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
160+
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
161+
BasicConstraintsValid: true,
162+
DNSNames: subjectAlternativeNames,
163+
}
164+
165+
certDerBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, &subjectPublicKey.PublicKey, issuerPrivateKey)
166+
c, err := x509.ParseCertificate(certDerBytes)
167+
if err != nil {
168+
panic(err)
169+
}
170+
return c
171+
172+
}
173+
174+
// toPEMFormat Converts an array of certificates to PEM format.
175+
func toPEMFormat(certs ...*x509.Certificate) ([]byte, error) {
176+
certPEM := new(bytes.Buffer)
177+
178+
for _, cert := range certs {
179+
err := pem.Encode(certPEM, &pem.Block{
180+
Type: "CERTIFICATE",
181+
Bytes: cert.Raw,
182+
})
183+
if err != nil {
184+
return nil, err
185+
}
186+
}
187+
188+
return certPEM.Bytes(), nil
189+
}
190+
191+
// signWithClientKey produces a PEM encoded certificate client certificate
192+
// containing the clientKey public key, signed by the client CA certificate.
193+
func (ct *TLSCertificates) signWithClientKey(clientKey *rsa.PublicKey) ([]byte, error) {
194+
notAfter := ct.clientCertExpires
195+
if ct.clientCertExpires.IsZero() {
196+
notAfter = time.Now().Add(1 * time.Hour)
197+
}
198+
199+
// Create a signed cert from the client's public key.
200+
cert := &x509.Certificate{ // TODO: Validate this format vs API
201+
SerialNumber: &big.Int{},
202+
Subject: pkix.Name{
203+
Country: []string{"US"},
204+
Organization: []string{"Google, Inc"},
205+
CommonName: "Google Cloud SQL Client",
206+
},
207+
NotBefore: time.Now(),
208+
NotAfter: notAfter,
209+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
210+
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
211+
BasicConstraintsValid: true,
212+
}
213+
certBytes, err := x509.CreateCertificate(rand.Reader, cert, ct.clientSigningCACertificate, clientKey, ct.clientSigningCaKey)
214+
if err != nil {
215+
return nil, err
216+
}
217+
certPEM := new(bytes.Buffer)
218+
err = pem.Encode(certPEM, &pem.Block{
219+
Type: "CERTIFICATE",
220+
Bytes: certBytes,
221+
})
222+
if err != nil {
223+
return nil, err
224+
}
225+
return certPEM.Bytes(), nil
226+
}
227+
228+
// generateServerCertWithCn generates a server certificate for legacy
229+
// GOOGLE_MANAGED_INTERNAL_CA mode where the instance name is in the CN.
230+
func (ct *TLSCertificates) generateServerCertWithCn(cn string) *x509.Certificate {
231+
return mustBuildSignedCertificate(
232+
false,
233+
name(cn),
234+
ct.serverKey,
235+
serverCaSubject,
236+
ct.serverCaKey,
237+
time.Now().Add(1*time.Hour), nil)
238+
}
239+
240+
// serverChain creates a []tls.Certificate for use with a TLS server socket.
241+
// serverCAMode controls whether this returns a legacy or CAS server
242+
// certificate.
243+
func (ct *TLSCertificates) serverChain(serverCAMode string) []tls.Certificate {
244+
// if this server is running in legacy mode
245+
if serverCAMode == "" || serverCAMode == "GOOGLE_MANAGED_INTERNAL_CA" {
246+
return []tls.Certificate{{
247+
Certificate: [][]byte{ct.serverCert.Raw, ct.serverCaCert.Raw},
248+
PrivateKey: ct.serverKey,
249+
Leaf: ct.serverCert,
250+
}}
251+
}
252+
253+
return []tls.Certificate{{
254+
Certificate: [][]byte{ct.casServerCertificate.Raw, ct.serverIntermediateCaCert.Raw, ct.serverCaCert.Raw},
255+
PrivateKey: ct.serverKey,
256+
Leaf: ct.casServerCertificate,
257+
}}
258+
259+
}
260+
func (ct *TLSCertificates) clientCAPool() *x509.CertPool {
261+
clientCa := x509.NewCertPool()
262+
clientCa.AddCert(ct.clientSigningCACertificate)
263+
return clientCa
264+
}
265+
266+
func (ct *TLSCertificates) rotateClientCA() {
267+
ct.clientSigningCaKey = mustGenerateKey()
268+
ct.clientSigningCACertificate = mustBuildRootCertificate(signingCaSubject, ct.clientSigningCaKey)
269+
}
270+
271+
func (ct *TLSCertificates) rotateCA() {
272+
oneYear := time.Now().AddDate(1, 0, 0)
273+
ct.serverCaKey = mustGenerateKey()
274+
ct.clientSigningCaKey = mustGenerateKey()
275+
ct.serverKey = mustGenerateKey()
276+
ct.serverIntermediateCaKey = mustGenerateKey()
277+
278+
ct.serverCaCert = mustBuildRootCertificate(serverCaSubject, ct.serverCaKey)
279+
280+
ct.serverIntermediateCaCert =
281+
mustBuildSignedCertificate(
282+
true,
283+
intermediateCaSubject,
284+
ct.serverIntermediateCaKey,
285+
serverCaSubject,
286+
ct.serverCaKey,
287+
oneYear,
288+
nil)
289+
290+
ct.casServerCertificate =
291+
mustBuildSignedCertificate(
292+
false,
293+
name(""),
294+
ct.serverKey,
295+
intermediateCaSubject,
296+
ct.serverIntermediateCaKey,
297+
oneYear,
298+
ct.sans)
299+
300+
ct.serverCert = mustBuildSignedCertificate(
301+
false,
302+
name(ct.projectName+":"+ct.instanceName),
303+
ct.serverKey,
304+
serverCaSubject,
305+
ct.serverCaKey,
306+
oneYear,
307+
nil)
308+
309+
ct.rotateClientCA()
310+
}

0 commit comments

Comments
 (0)