@@ -6,16 +6,22 @@ import (
66 "bufio"
77 "bytes"
88 "crypto/rand"
9+ "crypto/rsa"
910 "crypto/sha256"
11+ "crypto/tls"
12+ "crypto/x509"
13+ "crypto/x509/pkix"
1014 "encoding/base64"
1115 "encoding/hex"
1216 "encoding/json"
1317 "encoding/pem"
18+ "errors"
1419 "fmt"
1520 "io"
1621 "io/ioutil"
1722 "log"
1823 "math"
24+ "math/big"
1925 "net/http"
2026 "net/http/httptest"
2127 "net/textproto"
@@ -32,10 +38,11 @@ import (
3238)
3339
3440var (
35- repoDir string
36- largeObjects = newLfsStorage ()
37- server * httptest.Server
38- serverTLS * httptest.Server
41+ repoDir string
42+ largeObjects = newLfsStorage ()
43+ server * httptest.Server
44+ serverTLS * httptest.Server
45+ serverClientCert * httptest.Server
3946
4047 // maps OIDs to content strings. Both the LFS and Storage test servers below
4148 // see OIDs.
@@ -61,6 +68,22 @@ func main() {
6168 mux := http .NewServeMux ()
6269 server = httptest .NewServer (mux )
6370 serverTLS = httptest .NewTLSServer (mux )
71+ serverClientCert = httptest .NewUnstartedServer (mux )
72+
73+ //setup Client Cert server
74+ rootKey , rootCert := generateCARootCertificates ()
75+ _ , clientCertPEM , clientKeyPEM := generateClientCertificates (rootCert , rootKey )
76+
77+ certPool := x509 .NewCertPool ()
78+ certPool .AddCert (rootCert )
79+
80+ serverClientCert .TLS = & tls.Config {
81+ Certificates : []tls.Certificate {serverTLS .TLS .Certificates [0 ]},
82+ ClientAuth : tls .RequireAndVerifyClientCert ,
83+ ClientCAs : certPool ,
84+ }
85+ serverClientCert .StartTLS ()
86+
6487 ntlmSession , err := ntlm .CreateServerSession (ntlm .Version2 , ntlm .ConnectionOrientedMode )
6588 if err != nil {
6689 fmt .Println ("Error creating ntlm session:" , err )
@@ -108,15 +131,26 @@ func main() {
108131 sslurlname := writeTestStateFile ([]byte (serverTLS .URL ), "LFSTEST_SSL_URL" , "lfstest-gitserver-ssl" )
109132 defer os .RemoveAll (sslurlname )
110133
134+ clientCertUrlname := writeTestStateFile ([]byte (serverClientCert .URL ), "LFSTEST_CLIENT_CERT_URL" , "lfstest-gitserver-ssl" )
135+ defer os .RemoveAll (clientCertUrlname )
136+
111137 block := & pem.Block {}
112138 block .Type = "CERTIFICATE"
113139 block .Bytes = serverTLS .TLS .Certificates [0 ].Certificate [0 ]
114140 pembytes := pem .EncodeToMemory (block )
141+
115142 certname := writeTestStateFile (pembytes , "LFSTEST_CERT" , "lfstest-gitserver-cert" )
116143 defer os .RemoveAll (certname )
117144
145+ cccertname := writeTestStateFile (clientCertPEM , "LFSTEST_CLIENT_CERT" , "lfstest-gitserver-client-cert" )
146+ defer os .RemoveAll (cccertname )
147+
148+ ckcertname := writeTestStateFile (clientKeyPEM , "LFSTEST_CLIENT_KEY" , "lfstest-gitserver-client-key" )
149+ defer os .RemoveAll (ckcertname )
150+
118151 debug ("init" , "server url: %s" , server .URL )
119152 debug ("init" , "server tls url: %s" , serverTLS .URL )
153+ debug ("init" , "server client cert url: %s" , serverClientCert .URL )
120154
121155 <- stopch
122156 debug ("init" , "git server done" )
@@ -1212,3 +1246,95 @@ func reqId(w http.ResponseWriter) (string, bool) {
12121246 }
12131247 return fmt .Sprintf ("%x-%x-%x-%x-%x" , b [0 :4 ], b [4 :6 ], b [6 :8 ], b [8 :10 ], b [10 :]), true
12141248}
1249+
1250+ // https://ericchiang.github.io/post/go-tls/
1251+ func generateCARootCertificates () (rootKey * rsa.PrivateKey , rootCert * x509.Certificate ) {
1252+
1253+ // generate a new key-pair
1254+ rootKey , err := rsa .GenerateKey (rand .Reader , 2048 )
1255+ if err != nil {
1256+ log .Fatalf ("generating random key: %v" , err )
1257+ }
1258+
1259+ rootCertTmpl , err := CertTemplate ()
1260+ if err != nil {
1261+ log .Fatalf ("creating cert template: %v" , err )
1262+ }
1263+ // describe what the certificate will be used for
1264+ rootCertTmpl .IsCA = true
1265+ rootCertTmpl .KeyUsage = x509 .KeyUsageCertSign | x509 .KeyUsageDigitalSignature
1266+ rootCertTmpl .ExtKeyUsage = []x509.ExtKeyUsage {x509 .ExtKeyUsageServerAuth , x509 .ExtKeyUsageClientAuth }
1267+ // rootCertTmpl.IPAddresses = []net.IP{net.ParseIP("127.0.0.1")}
1268+
1269+ rootCert , _ , err = CreateCert (rootCertTmpl , rootCertTmpl , & rootKey .PublicKey , rootKey )
1270+
1271+ return
1272+ }
1273+
1274+ func generateClientCertificates (rootCert * x509.Certificate , rootKey interface {}) (clientKey * rsa.PrivateKey , clientCertPEM []byte , clientKeyPEM []byte ) {
1275+
1276+ // create a key-pair for the client
1277+ clientKey , err := rsa .GenerateKey (rand .Reader , 2048 )
1278+ if err != nil {
1279+ log .Fatalf ("generating random key: %v" , err )
1280+ }
1281+
1282+ // create a template for the client
1283+ clientCertTmpl , err1 := CertTemplate ()
1284+ if err1 != nil {
1285+ log .Fatalf ("creating cert template: %v" , err1 )
1286+ }
1287+ clientCertTmpl .KeyUsage = x509 .KeyUsageDigitalSignature
1288+ clientCertTmpl .ExtKeyUsage = []x509.ExtKeyUsage {x509 .ExtKeyUsageClientAuth }
1289+
1290+ // the root cert signs the cert by again providing its private key
1291+ _ , clientCertPEM , err2 := CreateCert (clientCertTmpl , rootCert , & clientKey .PublicKey , rootKey )
1292+ if err2 != nil {
1293+ log .Fatalf ("error creating cert: %v" , err2 )
1294+ }
1295+
1296+ // encode and load the cert and private key for the client
1297+ clientKeyPEM = pem .EncodeToMemory (& pem.Block {
1298+ Type : "RSA PRIVATE KEY" , Bytes : x509 .MarshalPKCS1PrivateKey (clientKey ),
1299+ })
1300+
1301+ return
1302+ }
1303+
1304+ // helper function to create a cert template with a serial number and other required fields
1305+ func CertTemplate () (* x509.Certificate , error ) {
1306+ // generate a random serial number (a real cert authority would have some logic behind this)
1307+ serialNumberLimit := new (big.Int ).Lsh (big .NewInt (1 ), 128 )
1308+ serialNumber , err := rand .Int (rand .Reader , serialNumberLimit )
1309+ if err != nil {
1310+ return nil , errors .New ("failed to generate serial number: " + err .Error ())
1311+ }
1312+
1313+ tmpl := x509.Certificate {
1314+ SerialNumber : serialNumber ,
1315+ Subject : pkix.Name {Organization : []string {"Yhat, Inc." }},
1316+ SignatureAlgorithm : x509 .SHA256WithRSA ,
1317+ NotBefore : time .Now (),
1318+ NotAfter : time .Now ().Add (time .Hour ), // valid for an hour
1319+ BasicConstraintsValid : true ,
1320+ }
1321+ return & tmpl , nil
1322+ }
1323+
1324+ func CreateCert (template , parent * x509.Certificate , pub interface {}, parentPriv interface {}) (
1325+ cert * x509.Certificate , certPEM []byte , err error ) {
1326+
1327+ certDER , err := x509 .CreateCertificate (rand .Reader , template , parent , pub , parentPriv )
1328+ if err != nil {
1329+ return
1330+ }
1331+ // parse the resulting certificate so we can use it again
1332+ cert , err = x509 .ParseCertificate (certDER )
1333+ if err != nil {
1334+ return
1335+ }
1336+ // PEM encode the certificate (this is a standard TLS encoding)
1337+ b := pem.Block {Type : "CERTIFICATE" , Bytes : certDER }
1338+ certPEM = pem .EncodeToMemory (& b )
1339+ return
1340+ }
0 commit comments