github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/certificate.go (about)

     1  /*
     2   * Copyright (c) 2018, Psiphon Inc.
     3   * All rights reserved.
     4   *
     5   * This program is free software: you can redistribute it and/or modify
     6   * it under the terms of the GNU General Public License as published by
     7   * the Free Software Foundation, either version 3 of the License, or
     8   * (at your option) any later version.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package common
    21  
    22  import (
    23  	"crypto/rand"
    24  	"crypto/rsa"
    25  	"crypto/sha1"
    26  	"crypto/x509"
    27  	"crypto/x509/pkix"
    28  	"encoding/pem"
    29  	"math/big"
    30  	"time"
    31  
    32  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
    33  )
    34  
    35  // GenerateWebServerCertificate creates a self-signed web server certificate,
    36  // using the specified host name (commonName).
    37  // This is primarily intended for use by MeekServer to generate on-the-fly,
    38  // self-signed TLS certificates for fronted HTTPS mode. In this case, the nature
    39  // of the certificate is non-circumvention; it only has to be acceptable to the
    40  // front CDN making connections to meek.
    41  // The same certificates are used for unfronted HTTPS meek. In this case, the
    42  // certificates may be a fingerprint used to detect Psiphon servers or traffic.
    43  // TODO: more effort to mitigate fingerprinting these certificates.
    44  //
    45  // In addition, GenerateWebServerCertificate is used by GenerateConfig to create
    46  // Psiphon web server certificates for test/example configurations. If these Psiphon
    47  // web server certificates are used in production, the same caveats about
    48  // fingerprints apply.
    49  func GenerateWebServerCertificate(commonName string) (string, string, error) {
    50  
    51  	// Based on https://golang.org/src/crypto/tls/generate_cert.go
    52  	// TODO: use other key types: anti-fingerprint by varying params
    53  
    54  	rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
    55  	if err != nil {
    56  		return "", "", errors.Trace(err)
    57  	}
    58  
    59  	// Validity period is 1 or 2 years, starting 1 to 6 months ago.
    60  	validityPeriodYears := 1
    61  	delta, err := rand.Int(rand.Reader, big.NewInt(2))
    62  	if err != nil {
    63  		return "", "", errors.Trace(err)
    64  	}
    65  	validityPeriodYears += int(delta.Int64())
    66  	retroactiveMonths := 1
    67  	delta, err = rand.Int(rand.Reader, big.NewInt(6))
    68  	if err != nil {
    69  		return "", "", errors.Trace(err)
    70  	}
    71  	retroactiveMonths += int(delta.Int64())
    72  	notBefore := time.Now().Truncate(time.Hour).UTC().AddDate(0, -retroactiveMonths, 0)
    73  	notAfter := notBefore.AddDate(validityPeriodYears, 0, 0)
    74  
    75  	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
    76  	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
    77  	if err != nil {
    78  		return "", "", errors.Trace(err)
    79  	}
    80  
    81  	publicKeyBytes, err := x509.MarshalPKIXPublicKey(rsaKey.Public())
    82  	if err != nil {
    83  		return "", "", errors.Trace(err)
    84  	}
    85  	// as per RFC3280 sec. 4.2.1.2
    86  	subjectKeyID := sha1.Sum(publicKeyBytes)
    87  
    88  	var subject pkix.Name
    89  	if commonName != "" {
    90  		subject = pkix.Name{CommonName: commonName}
    91  	}
    92  
    93  	template := x509.Certificate{
    94  		SerialNumber:          serialNumber,
    95  		Subject:               subject,
    96  		NotBefore:             notBefore,
    97  		NotAfter:              notAfter,
    98  		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
    99  		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   100  		BasicConstraintsValid: true,
   101  		IsCA:                  true,
   102  		SubjectKeyId:          subjectKeyID[:],
   103  		MaxPathLen:            1,
   104  		Version:               2,
   105  	}
   106  
   107  	derCert, err := x509.CreateCertificate(
   108  		rand.Reader,
   109  		&template,
   110  		&template,
   111  		rsaKey.Public(),
   112  		rsaKey)
   113  	if err != nil {
   114  		return "", "", errors.Trace(err)
   115  	}
   116  
   117  	webServerCertificate := pem.EncodeToMemory(
   118  		&pem.Block{
   119  			Type:  "CERTIFICATE",
   120  			Bytes: derCert,
   121  		},
   122  	)
   123  
   124  	webServerPrivateKey := pem.EncodeToMemory(
   125  		&pem.Block{
   126  			Type:  "RSA PRIVATE KEY",
   127  			Bytes: x509.MarshalPKCS1PrivateKey(rsaKey),
   128  		},
   129  	)
   130  
   131  	return string(webServerCertificate), string(webServerPrivateKey), nil
   132  }