Просмотр исходного кода

Move GenerateWebServerCertificate to common

Rod Hynes 7 лет назад
Родитель
Сommit
528f91e842

+ 126 - 0
psiphon/common/certificate.go

@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2018, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package common
+
+import (
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/sha1"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/pem"
+	"math/big"
+	"time"
+)
+
+// GenerateWebServerCertificate creates a self-signed web server certificate,
+// using the specified host name (commonName).
+// This is primarily intended for use by MeekServer to generate on-the-fly,
+// self-signed TLS certificates for fronted HTTPS mode. In this case, the nature
+// of the certificate is non-circumvention; it only has to be acceptable to the
+// front CDN making connections to meek.
+// The same certificates are used for unfronted HTTPS meek. In this case, the
+// certificates may be a fingerprint used to detect Psiphon servers or traffic.
+// TODO: more effort to mitigate fingerprinting these certificates.
+//
+// In addition, GenerateWebServerCertificate is used by GenerateConfig to create
+// Psiphon web server certificates for test/example configurations. If these Psiphon
+// web server certificates are used in production, the same caveats about
+// fingerprints apply.
+func GenerateWebServerCertificate(commonName string) (string, string, error) {
+
+	// Based on https://golang.org/src/crypto/tls/generate_cert.go
+	// TODO: use other key types: anti-fingerprint by varying params
+
+	rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
+	if err != nil {
+		return "", "", ContextError(err)
+	}
+
+	// Validity period is ~10 years, starting some number of ~months
+	// back in the last year.
+
+	age, err := MakeSecureRandomInt(12)
+	if err != nil {
+		return "", "", ContextError(err)
+	}
+	age += 1
+	validityPeriod := 10 * 365 * 24 * time.Hour
+	notBefore := time.Now().Add(time.Duration(-age) * 30 * 24 * time.Hour).UTC()
+	notAfter := notBefore.Add(validityPeriod).UTC()
+
+	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
+	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
+	if err != nil {
+		return "", "", ContextError(err)
+	}
+
+	publicKeyBytes, err := x509.MarshalPKIXPublicKey(rsaKey.Public())
+	if err != nil {
+		return "", "", ContextError(err)
+	}
+	// as per RFC3280 sec. 4.2.1.2
+	subjectKeyID := sha1.Sum(publicKeyBytes)
+
+	var subject pkix.Name
+	if commonName != "" {
+		subject = pkix.Name{CommonName: commonName}
+	}
+
+	template := x509.Certificate{
+		SerialNumber:          serialNumber,
+		Subject:               subject,
+		NotBefore:             notBefore,
+		NotAfter:              notAfter,
+		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
+		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
+		BasicConstraintsValid: true,
+		IsCA:         true,
+		SubjectKeyId: subjectKeyID[:],
+		MaxPathLen:   1,
+		Version:      2,
+	}
+
+	derCert, err := x509.CreateCertificate(
+		rand.Reader,
+		&template,
+		&template,
+		rsaKey.Public(),
+		rsaKey)
+	if err != nil {
+		return "", "", ContextError(err)
+	}
+
+	webServerCertificate := pem.EncodeToMemory(
+		&pem.Block{
+			Type:  "CERTIFICATE",
+			Bytes: derCert,
+		},
+	)
+
+	webServerPrivateKey := pem.EncodeToMemory(
+		&pem.Block{
+			Type:  "RSA PRIVATE KEY",
+			Bytes: x509.MarshalPKCS1PrivateKey(rsaKey),
+		},
+	)
+
+	return string(webServerCertificate), string(webServerPrivateKey), nil
+}

+ 38 - 0
psiphon/common/certificate_test.go

@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package common
+
+import (
+	"crypto/tls"
+	"testing"
+)
+
+func TestGenerateWebServerCertificate(t *testing.T) {
+
+	certificate, privateKey, err := GenerateWebServerCertificate("www.example.com")
+	if err != nil {
+		t.Errorf("GenerateWebServerCertificate failed: %s", err)
+	}
+
+	_, err = tls.X509KeyPair([]byte(certificate), []byte(privateKey))
+	if err != nil {
+		t.Errorf("tls.X509KeyPair failed: %s", err)
+	}
+}

+ 1 - 1
psiphon/server/config.go

@@ -513,7 +513,7 @@ func GenerateConfig(params *GenerateConfigParams) ([]byte, []byte, []byte, []byt
 			return nil, nil, nil, nil, nil, common.ContextError(err)
 		}
 
-		webServerCertificate, webServerPrivateKey, err = GenerateWebServerCertificate("")
+		webServerCertificate, webServerPrivateKey, err = common.GenerateWebServerCertificate("")
 		if err != nil {
 			return nil, nil, nil, nil, nil, common.ContextError(err)
 		}

+ 1 - 1
psiphon/server/meek.go

@@ -818,7 +818,7 @@ func makeMeekTLSConfig(
 	support *SupportServices,
 	useObfuscatedSessionTickets bool) (*utls.Config, error) {
 
-	certificate, privateKey, err := GenerateWebServerCertificate(common.GenerateHostName())
+	certificate, privateKey, err := common.GenerateWebServerCertificate(common.GenerateHostName())
 	if err != nil {
 		return nil, common.ContextError(err)
 	}

+ 0 - 105
psiphon/server/utils.go

@@ -20,116 +20,11 @@
 package server
 
 import (
-	"crypto/rand"
-	"crypto/rsa"
-	"crypto/sha1"
-	"crypto/x509"
-	"crypto/x509/pkix"
-	"encoding/pem"
 	"fmt"
 	"io"
-	"math/big"
 	"sync/atomic"
-	"time"
-
-	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
 )
 
-// GenerateWebServerCertificate creates a self-signed web server certificate,
-// using the specified host name (commonName).
-// This is primarily intended for use by MeekServer to generate on-the-fly,
-// self-signed TLS certificates for fronted HTTPS mode. In this case, the nature
-// of the certificate is non-circumvention; it only has to be acceptable to the
-// front CDN making connections to meek.
-// The same certificates are used for unfronted HTTPS meek. In this case, the
-// certificates may be a fingerprint used to detect Psiphon servers or traffic.
-// TODO: more effort to mitigate fingerprinting these certificates.
-//
-// In addition, GenerateWebServerCertificate is used by GenerateConfig to create
-// Psiphon web server certificates for test/example configurations. If these Psiphon
-// web server certificates are used in production, the same caveats about
-// fingerprints apply.
-func GenerateWebServerCertificate(commonName string) (string, string, error) {
-
-	// Based on https://golang.org/src/crypto/tls/generate_cert.go
-	// TODO: use other key types: anti-fingerprint by varying params
-
-	rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
-	if err != nil {
-		return "", "", common.ContextError(err)
-	}
-
-	// Validity period is ~10 years, starting some number of ~months
-	// back in the last year.
-
-	age, err := common.MakeSecureRandomInt(12)
-	if err != nil {
-		return "", "", common.ContextError(err)
-	}
-	age += 1
-	validityPeriod := 10 * 365 * 24 * time.Hour
-	notBefore := time.Now().Add(time.Duration(-age) * 30 * 24 * time.Hour).UTC()
-	notAfter := notBefore.Add(validityPeriod).UTC()
-
-	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
-	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
-	if err != nil {
-		return "", "", common.ContextError(err)
-	}
-
-	publicKeyBytes, err := x509.MarshalPKIXPublicKey(rsaKey.Public())
-	if err != nil {
-		return "", "", common.ContextError(err)
-	}
-	// as per RFC3280 sec. 4.2.1.2
-	subjectKeyID := sha1.Sum(publicKeyBytes)
-
-	var subject pkix.Name
-	if commonName != "" {
-		subject = pkix.Name{CommonName: commonName}
-	}
-
-	template := x509.Certificate{
-		SerialNumber:          serialNumber,
-		Subject:               subject,
-		NotBefore:             notBefore,
-		NotAfter:              notAfter,
-		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
-		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
-		BasicConstraintsValid: true,
-		IsCA:         true,
-		SubjectKeyId: subjectKeyID[:],
-		MaxPathLen:   1,
-		Version:      2,
-	}
-
-	derCert, err := x509.CreateCertificate(
-		rand.Reader,
-		&template,
-		&template,
-		rsaKey.Public(),
-		rsaKey)
-	if err != nil {
-		return "", "", common.ContextError(err)
-	}
-
-	webServerCertificate := pem.EncodeToMemory(
-		&pem.Block{
-			Type:  "CERTIFICATE",
-			Bytes: derCert,
-		},
-	)
-
-	webServerPrivateKey := pem.EncodeToMemory(
-		&pem.Block{
-			Type:  "RSA PRIVATE KEY",
-			Bytes: x509.MarshalPKCS1PrivateKey(rsaKey),
-		},
-	)
-
-	return string(webServerCertificate), string(webServerPrivateKey), nil
-}
-
 // IntentionalPanicError is an error type that is used
 // when calling panic() in a situation where recovers
 // should propagate the panic.