Skip to content

Commit 3a7c320

Browse files
committed
test: Correct the server and client certificates used in dialer_test.go
1 parent 9261b48 commit 3a7c320

File tree

4 files changed

+372
-147
lines changed

4 files changed

+372
-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: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
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+
ServerCaKeyPair *rsa.PrivateKey
52+
SigningCaKeyPair *rsa.PrivateKey
53+
ServerKeyPair *rsa.PrivateKey
54+
ServerIntermediateCaKeyPair *rsa.PrivateKey
55+
ServerSigningCaKeyPair *rsa.PrivateKey
56+
ClientKeyPair *rsa.PrivateKey
57+
DomainServerKeyPair *rsa.PrivateKey
58+
59+
ServerCaCert *x509.Certificate
60+
SigningCaCert *x509.Certificate
61+
ServerCert *x509.Certificate
62+
ServerIntermediateCaCert *x509.Certificate
63+
CasServerCertificate *x509.Certificate
64+
CasServerCertificateChain []*x509.Certificate
65+
DomainServerCertificate *x509.Certificate
66+
clientCertExpires time.Time
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+
oneYear := time.Now().AddDate(1, 0, 0)
80+
81+
c := &TLSCertificates{
82+
clientCertExpires: clientCertExpires,
83+
ServerCaKeyPair: mustGenerateKey(),
84+
SigningCaKeyPair: mustGenerateKey(),
85+
ServerKeyPair: mustGenerateKey(),
86+
ServerIntermediateCaKeyPair: mustGenerateKey(),
87+
ServerSigningCaKeyPair: mustGenerateKey(),
88+
ClientKeyPair: mustGenerateKey(),
89+
DomainServerKeyPair: mustGenerateKey(),
90+
}
91+
92+
c.ServerCaCert = mustBuildRootCertificate(serverCaSubject, c.ServerCaKeyPair)
93+
c.SigningCaCert = mustBuildRootCertificate(signingCaSubject, c.SigningCaKeyPair)
94+
95+
c.ServerCert = mustBuildSignedCertificate(
96+
false,
97+
name(projectName+":"+instanceName),
98+
c.ServerKeyPair,
99+
serverCaSubject,
100+
c.ServerCaKeyPair,
101+
oneYear,
102+
nil)
103+
104+
c.ServerIntermediateCaCert =
105+
mustBuildSignedCertificate(
106+
true,
107+
intermediateCaSubject,
108+
c.ServerIntermediateCaKeyPair,
109+
serverCaSubject,
110+
c.ServerCaKeyPair,
111+
oneYear,
112+
nil)
113+
114+
c.CasServerCertificate =
115+
mustBuildSignedCertificate(
116+
false,
117+
name(""),
118+
c.ServerKeyPair,
119+
intermediateCaSubject,
120+
c.ServerIntermediateCaKeyPair,
121+
oneYear,
122+
sans)
123+
124+
c.CasServerCertificateChain =
125+
[]*x509.Certificate{
126+
c.CasServerCertificate, c.ServerIntermediateCaCert, c.ServerCaCert}
127+
128+
return c
129+
}
130+
131+
// generateSKI Generate public key id. Certificates need to include
132+
// the key id to make the certificate chain work.
133+
func generateSKI(pub *rsa.PublicKey) []byte {
134+
bs := make([]byte, 8)
135+
binary.LittleEndian.PutUint64(bs, uint64(pub.E))
136+
137+
hasher := sha1.New()
138+
hasher.Write(bs)
139+
if pub.N != nil {
140+
hasher.Write(pub.N.Bytes())
141+
}
142+
ski := hasher.Sum(nil)
143+
144+
return ski
145+
}
146+
147+
// mustBuildRootCertificate produces a self-signed certificate.
148+
// or panics - use only for testing.
149+
func mustBuildRootCertificate(subject pkix.Name, k *rsa.PrivateKey) *x509.Certificate {
150+
151+
sn, err := rand.Int(rand.Reader, big.NewInt(1000))
152+
if err != nil {
153+
panic(err)
154+
}
155+
156+
cert := &x509.Certificate{
157+
SerialNumber: sn,
158+
SubjectKeyId: generateSKI(&k.PublicKey),
159+
Subject: subject,
160+
NotBefore: time.Now(),
161+
NotAfter: time.Now().AddDate(1, 0, 0),
162+
IsCA: true,
163+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
164+
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
165+
BasicConstraintsValid: true,
166+
}
167+
168+
certDerBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, &k.PublicKey, k)
169+
c, err := x509.ParseCertificate(certDerBytes)
170+
if err != nil {
171+
panic(err)
172+
}
173+
return c
174+
}
175+
176+
// mustBuildSignedCertificate produces a certificate for Subject that is signed
177+
// by the issuer.
178+
func mustBuildSignedCertificate(
179+
isCa bool,
180+
subject pkix.Name,
181+
subjectPublicKey *rsa.PrivateKey,
182+
certificateIssuer pkix.Name,
183+
issuerPrivateKey *rsa.PrivateKey,
184+
notAfter time.Time,
185+
subjectAlternativeNames []string) *x509.Certificate {
186+
187+
sn, err := rand.Int(rand.Reader, big.NewInt(1000))
188+
if err != nil {
189+
panic(err)
190+
}
191+
192+
cert := &x509.Certificate{
193+
SerialNumber: sn,
194+
Subject: subject,
195+
SubjectKeyId: generateSKI(&subjectPublicKey.PublicKey),
196+
AuthorityKeyId: generateSKI(&issuerPrivateKey.PublicKey),
197+
Issuer: certificateIssuer,
198+
NotBefore: time.Now(),
199+
NotAfter: notAfter,
200+
IsCA: isCa,
201+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
202+
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
203+
BasicConstraintsValid: true,
204+
DNSNames: subjectAlternativeNames,
205+
}
206+
207+
certDerBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, &subjectPublicKey.PublicKey, issuerPrivateKey)
208+
c, err := x509.ParseCertificate(certDerBytes)
209+
if err != nil {
210+
panic(err)
211+
}
212+
return c
213+
214+
}
215+
216+
// toPEMFormat Converts an array of certificates to PEM format.
217+
func toPEMFormat(certs ...*x509.Certificate) ([]byte, error) {
218+
certPEM := new(bytes.Buffer)
219+
220+
for _, cert := range certs {
221+
err := pem.Encode(certPEM, &pem.Block{
222+
Type: "CERTIFICATE",
223+
Bytes: cert.Raw,
224+
})
225+
if err != nil {
226+
return nil, err
227+
}
228+
}
229+
230+
return certPEM.Bytes(), nil
231+
}
232+
233+
// signWithClientKey produces a PEM encoded certificate client certificate
234+
// containing the clientKey public key, signed by the client CA certificate.
235+
func (ct *TLSCertificates) signWithClientKey(clientKey *rsa.PublicKey) ([]byte, error) {
236+
notAfter := ct.clientCertExpires
237+
if ct.clientCertExpires.IsZero() {
238+
notAfter = time.Now().Add(1 * time.Hour)
239+
}
240+
241+
// Create a signed cert from the client's public key.
242+
cert := &x509.Certificate{ // TODO: Validate this format vs API
243+
SerialNumber: &big.Int{},
244+
Subject: pkix.Name{
245+
Country: []string{"US"},
246+
Organization: []string{"Google, Inc"},
247+
CommonName: "Google Cloud SQL Client",
248+
},
249+
NotBefore: time.Now(),
250+
NotAfter: notAfter,
251+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
252+
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
253+
BasicConstraintsValid: true,
254+
}
255+
certBytes, err := x509.CreateCertificate(rand.Reader, cert, ct.SigningCaCert, clientKey, ct.SigningCaKeyPair)
256+
if err != nil {
257+
return nil, err
258+
}
259+
certPEM := new(bytes.Buffer)
260+
err = pem.Encode(certPEM, &pem.Block{
261+
Type: "CERTIFICATE",
262+
Bytes: certBytes,
263+
})
264+
if err != nil {
265+
return nil, err
266+
}
267+
return certPEM.Bytes(), nil
268+
}
269+
270+
// generateServerCertWithCn generates a server certificate for legacy
271+
// GOOGLE_MANAGED_INTERNAL_CA mode where the instance name is in the CN.
272+
func (ct *TLSCertificates) generateServerCertWithCn(cn string) *x509.Certificate {
273+
return mustBuildSignedCertificate(
274+
false,
275+
name(cn),
276+
ct.ServerKeyPair,
277+
serverCaSubject,
278+
ct.ServerCaKeyPair,
279+
time.Now().Add(1*time.Hour), nil)
280+
}
281+
282+
// serverChain creates a []tls.Certificate for use with a TLS server socket.
283+
// serverCAMode controls whether this returns a legacy or CAS server
284+
// certificate.
285+
func (ct *TLSCertificates) serverChain(serverCAMode string) []tls.Certificate {
286+
// if this server is running in legacy mode
287+
if serverCAMode == "" || serverCAMode == "GOOGLE_MANAGED_INTERNAL_CA" {
288+
return []tls.Certificate{{
289+
Certificate: [][]byte{ct.ServerCert.Raw, ct.ServerCaCert.Raw},
290+
PrivateKey: ct.ServerKeyPair,
291+
Leaf: ct.ServerCert,
292+
}}
293+
}
294+
295+
return []tls.Certificate{{
296+
Certificate: [][]byte{ct.CasServerCertificate.Raw, ct.ServerIntermediateCaCert.Raw, ct.ServerCaCert.Raw},
297+
PrivateKey: ct.ServerKeyPair,
298+
Leaf: ct.CasServerCertificate,
299+
}}
300+
301+
}

0 commit comments

Comments
 (0)