Skip to content

Commit bfe2804

Browse files
committed
adding self certificate generation
1 parent e1b3244 commit bfe2804

File tree

4 files changed

+208
-2
lines changed

4 files changed

+208
-2
lines changed

pkg/sslcert/options.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package sslcert
2+
3+
import "time"
4+
5+
type Options struct {
6+
Host string // Comma-separated hostnames and IPs to generate a certificate for")
7+
Organization string
8+
ValidFrom string // Creation date formatted as Jan 1 15:04:05 2011
9+
ValidFor time.Duration // 365*24*time.Hour Duration that certificate is valid for
10+
IsCA bool // whether this cert should be its own Certificate Authority
11+
RSABits int // 2048 Size of RSA key to generate. Ignored if --ecdsa-curve is set
12+
EcdsaCurve string // ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521
13+
Ed25519Key bool // Generate an Ed25519 key
14+
}
15+
16+
var DefaultOptions = Options{
17+
ValidFor: time.Duration(365 * 24 * time.Hour),
18+
IsCA: false,
19+
RSABits: 2048,
20+
Organization: "Acme Co",
21+
}

pkg/sslcert/sslcert.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// Package sslcert contains a reworked version of https://golang.org/src/crypto/tls/generate_cert.go
2+
package sslcert
3+
4+
import (
5+
"bufio"
6+
"bytes"
7+
"crypto/ecdsa"
8+
"crypto/ed25519"
9+
"crypto/elliptic"
10+
"crypto/rand"
11+
"crypto/rsa"
12+
"crypto/x509"
13+
"crypto/x509/pkix"
14+
"encoding/pem"
15+
"errors"
16+
"fmt"
17+
"math/big"
18+
"net"
19+
"strings"
20+
"time"
21+
)
22+
23+
func pubKey(priv interface{}) interface{} {
24+
switch k := priv.(type) {
25+
case *rsa.PrivateKey:
26+
return &k.PublicKey
27+
case *ecdsa.PrivateKey:
28+
return &k.PublicKey
29+
case ed25519.PrivateKey:
30+
return k.Public().(ed25519.PublicKey)
31+
default:
32+
return nil
33+
}
34+
}
35+
36+
func Generate(options Options) (privateKey, publicKey []byte, err error) {
37+
if options.Host == "" {
38+
return nil, nil, errors.New("Empty host value")
39+
}
40+
41+
var priv interface{}
42+
switch options.EcdsaCurve {
43+
case "":
44+
if options.Ed25519Key {
45+
_, priv, err = ed25519.GenerateKey(rand.Reader)
46+
} else {
47+
priv, err = rsa.GenerateKey(rand.Reader, options.RSABits)
48+
}
49+
case "P224":
50+
priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
51+
case "P256":
52+
priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
53+
case "P384":
54+
priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
55+
case "P521":
56+
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
57+
default:
58+
err = fmt.Errorf("Unrecognized elliptic curve: %q", options.EcdsaCurve)
59+
return
60+
}
61+
if err != nil {
62+
err = fmt.Errorf("Failed to generate private key: %v", err)
63+
return
64+
}
65+
66+
// ECDSA, ED25519 and RSA subject keys should have the DigitalSignature
67+
// KeyUsage bits set in the x509.Certificate template
68+
keyUsage := x509.KeyUsageDigitalSignature
69+
// Only RSA subject keys should have the KeyEncipherment KeyUsage bits set. In
70+
// the context of TLS this KeyUsage is particular to RSA key exchange and
71+
// authentication.
72+
if _, isRSA := priv.(*rsa.PrivateKey); isRSA {
73+
keyUsage |= x509.KeyUsageKeyEncipherment
74+
}
75+
76+
var notBefore time.Time
77+
if len(options.ValidFrom) == 0 {
78+
notBefore = time.Now()
79+
} else {
80+
notBefore, err = time.Parse("Jan 2 15:04:05 2006", options.ValidFrom)
81+
if err != nil {
82+
err = fmt.Errorf("Failed to parse creation date: %v", err)
83+
return
84+
}
85+
}
86+
87+
notAfter := notBefore.Add(options.ValidFor)
88+
89+
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
90+
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
91+
if err != nil {
92+
err = fmt.Errorf("Failed to generate serial number: %v", err)
93+
return
94+
}
95+
96+
template := x509.Certificate{
97+
SerialNumber: serialNumber,
98+
Subject: pkix.Name{
99+
Organization: []string{options.Organization},
100+
},
101+
NotBefore: notBefore,
102+
NotAfter: notAfter,
103+
104+
KeyUsage: keyUsage,
105+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
106+
BasicConstraintsValid: true,
107+
}
108+
109+
hosts := strings.Split(options.Host, ",")
110+
for _, h := range hosts {
111+
if ip := net.ParseIP(h); ip != nil {
112+
template.IPAddresses = append(template.IPAddresses, ip)
113+
} else {
114+
template.DNSNames = append(template.DNSNames, h)
115+
}
116+
}
117+
118+
if options.IsCA {
119+
template.IsCA = true
120+
template.KeyUsage |= x509.KeyUsageCertSign
121+
}
122+
123+
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, pubKey(priv), priv)
124+
if err != nil {
125+
err = fmt.Errorf("Failed to create certificate: %v", err)
126+
return
127+
}
128+
129+
var pubKeyBuf bytes.Buffer
130+
pubKeyBufb := bufio.NewWriter(&pubKeyBuf)
131+
err = pem.Encode(pubKeyBufb, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
132+
if err != nil {
133+
err = fmt.Errorf("Failed to write data to cert.pem: %v", err)
134+
return
135+
}
136+
137+
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
138+
if err != nil {
139+
err = fmt.Errorf("Unable to marshal private key: %v", err)
140+
return
141+
}
142+
var privKeyBuf bytes.Buffer
143+
privKeyBufb := bufio.NewWriter(&privKeyBuf)
144+
err = pem.Encode(privKeyBufb, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes})
145+
if err != nil {
146+
err = fmt.Errorf("Failed to write data to key.pem: %v", err)
147+
return
148+
}
149+
150+
return pubKeyBuf.Bytes(), privKeyBuf.Bytes(), nil
151+
}

pkg/sslcert/tlsconfig.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package sslcert
2+
3+
import (
4+
"crypto/tls"
5+
)
6+
7+
func NewTLSConfig(options Options) (*tls.Config, error) {
8+
pubKey, privKey, err := Generate(options)
9+
if err != nil {
10+
return nil, err
11+
}
12+
13+
cert, err := tls.X509KeyPair(pubKey, privKey)
14+
if err != nil {
15+
return nil, err
16+
}
17+
18+
return &tls.Config{Certificates: []tls.Certificate{cert}}, nil
19+
}

simplehttpserver.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"path"
1212

1313
"github.com/projectdiscovery/gologger"
14+
"github.com/projectdiscovery/simplehttpserver/pkg/sslcert"
1415
)
1516

1617
type options struct {
@@ -21,6 +22,7 @@ type options struct {
2122
Realm string
2223
Certificate string
2324
Key string
25+
Domain string
2426
HTTPS bool
2527
Verbose bool
2628
Upload bool
@@ -35,6 +37,7 @@ func main() {
3537
flag.BoolVar(&opts.HTTPS, "https", false, "HTTPS")
3638
flag.StringVar(&opts.Certificate, "cert", "", "Certificate")
3739
flag.StringVar(&opts.Key, "key", "", "Key")
40+
flag.StringVar(&opts.Domain, "domain", "", "Domain")
3841
flag.BoolVar(&opts.Verbose, "v", false, "Verbose")
3942
flag.StringVar(&opts.Username, "username", "", "Basic auth username")
4043
flag.StringVar(&opts.Password, "password", "", "Basic auth password")
@@ -57,9 +60,21 @@ func main() {
5760
}
5861
if opts.HTTPS {
5962
if opts.Certificate == "" || opts.Key == "" {
60-
gologger.Fatal().Msgf("Certificate or Key file not specified")
63+
tlsOptions := sslcert.DefaultOptions
64+
tlsOptions.Host = opts.Domain
65+
tlsConfig, err := sslcert.NewTLSConfig(tlsOptions)
66+
if err != nil {
67+
gologger.Fatal().Msgf("%s\n", err)
68+
}
69+
httpServer := &http.Server{
70+
Addr: opts.ListenAddress,
71+
TLSConfig: tlsConfig,
72+
}
73+
httpServer.Handler = layers
74+
gologger.Print().Msgf("%s\n", httpServer.ListenAndServeTLS("", ""))
75+
} else {
76+
gologger.Print().Msgf("%s\n", http.ListenAndServeTLS(opts.ListenAddress, opts.Certificate, opts.Key, layers))
6177
}
62-
gologger.Print().Msgf("%s\n", http.ListenAndServeTLS(opts.ListenAddress, opts.Certificate, opts.Key, layers))
6378
} else {
6479
gologger.Print().Msgf("%s\n", http.ListenAndServe(opts.ListenAddress, layers))
6580
}

0 commit comments

Comments
 (0)