@@ -21,6 +21,7 @@ import (
2121 "math/big"
2222 "os"
2323 "path/filepath"
24+ "slices"
2425 "sync/atomic"
2526 "testing"
2627 "time"
@@ -222,8 +223,8 @@ func PublicKey(key crypto.PrivateKey) interface{ Equal(x crypto.PublicKey) bool
222223}
223224
224225// CreateSelfSignedTLSCertificate creates a self-signed TLS certificate.
225- func CreateSelfSignedTLSCertificate (key interface {}) (* tls.Certificate , error ) {
226- c , err := CreateSelfSignedCertificate (key )
226+ func CreateSelfSignedTLSCertificate (key interface {}, opts ... CertificateOpts ) (* tls.Certificate , error ) {
227+ c , err := CreateSelfSignedCertificate (key , opts ... )
227228 if err != nil {
228229 return nil , err
229230 }
@@ -244,7 +245,7 @@ func CreateSelfSignedTLSCertificate(key interface{}) (*tls.Certificate, error) {
244245}
245246
246247// CreateSelfSignedCertificate creates a self-signed x509 certificate.
247- func CreateSelfSignedCertificate (key interface {}) (cert * x509.Certificate , err error ) {
248+ func CreateSelfSignedCertificate (key interface {}, opts ... CertificateOpts ) (cert * x509.Certificate , err error ) {
248249 serialNumberLimit := new (big.Int ).Lsh (big .NewInt (1 ), 128 )
249250 serialNumber , err := rand .Int (rand .Reader , serialNumberLimit )
250251 if err != nil {
@@ -263,14 +264,16 @@ func CreateSelfSignedCertificate(key interface{}) (cert *x509.Certificate, err e
263264 },
264265 NotBefore : time .Now ().UTC (),
265266 NotAfter : time .Now ().UTC ().Add (time .Hour * 24 * 31 ),
266- KeyUsage : x509 .KeyUsageKeyEncipherment | x509 .KeyUsageDigitalSignature ,
267- ExtKeyUsage : []x509.ExtKeyUsage {x509 .ExtKeyUsageServerAuth },
267+ KeyUsage : x509 .KeyUsageKeyEncipherment | x509 .KeyUsageDigitalSignature | x509 . KeyUsageCertSign ,
268+ ExtKeyUsage : []x509.ExtKeyUsage {x509 .ExtKeyUsageServerAuth , x509 . ExtKeyUsageClientAuth },
268269 BasicConstraintsValid : true ,
270+ IsCA : true ,
271+ DNSNames : []string {"localhost" },
272+ }
273+ for _ , opt := range opts {
274+ opt (certificate )
269275 }
270276
271- certificate .IsCA = true
272- certificate .KeyUsage |= x509 .KeyUsageCertSign
273- certificate .DNSNames = append (certificate .DNSNames , "localhost" )
274277 der , err := x509 .CreateCertificate (rand .Reader , certificate , certificate , PublicKey (key ), key )
275278 if err != nil {
276279 return cert , errors .Errorf ("failed to create certificate: %s" , err )
@@ -292,6 +295,61 @@ func PEMBlockForKey(key interface{}) (*pem.Block, error) {
292295 return & pem.Block {Type : "PRIVATE KEY" , Bytes : b }, nil
293296}
294297
298+ // NewClientCert creates a new client TLS certificate signed by the given CA.
299+ func NewClientCert (CAcert * x509.Certificate , CAkey crypto.PrivateKey , opts ... CertificateOpts ) (* tls.Certificate , error ) {
300+ if ! slices .Contains (CAcert .ExtKeyUsage , x509 .ExtKeyUsageClientAuth ) {
301+ return nil , errors .Errorf ("the CA certificate does not have the client authentication extended key usage (OID 1.3.6.1.5.5.7.3.2) set" )
302+ }
303+ serialNumberLimit := new (big.Int ).Lsh (big .NewInt (1 ), 128 )
304+ serialNumber , err := rand .Int (rand .Reader , serialNumberLimit )
305+ if err != nil {
306+ return nil , errors .Errorf ("failed to generate serial number: %s" , err )
307+ }
308+
309+ key , err := rsa .GenerateKey (rand .Reader , 3072 )
310+ if err != nil {
311+ return nil , errors .Errorf ("failed to generate private key: %s" , err )
312+ }
313+
314+ template := & x509.Certificate {
315+ SerialNumber : serialNumber ,
316+ Subject : pkix.Name {
317+ Organization : []string {"Ory GmbH" },
318+ CommonName : "ORY" ,
319+ },
320+ Issuer : CAcert .Subject ,
321+ NotBefore : time .Now ().UTC (),
322+ NotAfter : CAcert .NotAfter ,
323+ KeyUsage : x509 .KeyUsageDigitalSignature | x509 .KeyUsageKeyEncipherment ,
324+ ExtKeyUsage : []x509.ExtKeyUsage {x509 .ExtKeyUsageClientAuth },
325+ BasicConstraintsValid : true ,
326+ IsCA : false ,
327+ }
328+ for _ , opt := range opts {
329+ opt (template )
330+ }
331+
332+ der , err := x509 .CreateCertificate (rand .Reader , template , CAcert , PublicKey (key ), CAkey )
333+ if err != nil {
334+ return nil , errors .Errorf ("failed to create certificate: %s" , err )
335+ }
336+
337+ pemCert := pem .EncodeToMemory (& pem.Block {Type : "CERTIFICATE" , Bytes : der })
338+ pemBlock , err := PEMBlockForKey (key )
339+ if err != nil {
340+ return nil , err
341+ }
342+ pemKey := pem .EncodeToMemory (pemBlock )
343+
344+ cert , err := tls .X509KeyPair (pemCert , pemKey )
345+ if err != nil {
346+ return nil , errors .WithStack (err )
347+ }
348+ return & cert , nil
349+ }
350+
351+ type CertificateOpts func (* x509.Certificate )
352+
295353// CreateSelfSignedCertificateForTest writes a new, self-signed TLS
296354// certificate+key (in PEM format) to a temporary location on disk and returns
297355// the paths to both, and the respective contents in base64 encoding. The
0 commit comments