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 }