mirror of
https://github.com/go-i2p/onramp.git
synced 2025-07-01 04:59:41 -04:00
also add convenience functions for TLS
This commit is contained in:
12
DOCS.md
12
DOCS.md
@ -62,6 +62,10 @@ objects instantiated by an app.
|
||||
CloseOnion closes the Onion at the given index. It does not affect Onion
|
||||
objects instantiated by an app.
|
||||
|
||||
### func [CreateTLSCertificate](/tls.go#L20)
|
||||
|
||||
`func CreateTLSCertificate(tlsHost string, tlsCert, tlsKey *string) error`
|
||||
|
||||
### func [DeleteGarlicKeys](/garlic.go#L159)
|
||||
|
||||
`func DeleteGarlicKeys(tunName string) error`
|
||||
@ -160,6 +164,14 @@ ListenOnion returns a net.Listener for a onion structure's keys
|
||||
corresponding to a structure managed by the onramp library
|
||||
and not instantiated by an app.
|
||||
|
||||
### func [NewTLSCertificate](/tls.go#L114)
|
||||
|
||||
`func NewTLSCertificate(host string, priv *ecdsa.PrivateKey) ([]byte, error)`
|
||||
|
||||
### func [NewTLSCertificateAltNames](/tls.go#L118)
|
||||
|
||||
`func NewTLSCertificateAltNames(priv *ecdsa.PrivateKey, hosts ...string) ([]byte, error)`
|
||||
|
||||
### func [TorKeys](/onion.go#L135)
|
||||
|
||||
`func TorKeys(keyName string) (ed25519.KeyPair, error)`
|
||||
|
28
common.go
28
common.go
@ -32,17 +32,23 @@ func GetJoinedWD(dir string) (string, error) {
|
||||
|
||||
var i2pdefault, i2pkserr = GetJoinedWD("i2pkeys")
|
||||
var tordefault, torkserr = GetJoinedWD("onionkeys")
|
||||
var tlsdefault, tlskserr = GetJoinedWD("tlskeys")
|
||||
|
||||
// I2P_KEYSTORE_PATH is the place where I2P Keys will be saved.
|
||||
// it defaults to the directory "i2pkeys" current working directory
|
||||
// it defaults to the directory "i2pkeys" current working directory.
|
||||
// reference it by calling I2PKeystorePath() to check for errors
|
||||
var I2P_KEYSTORE_PATH = i2pdefault
|
||||
|
||||
// ONION_KEYSTORE_PATH is the place where Onion Keys will be saved.
|
||||
// it defaults to the directory "onionkeys" current working directory
|
||||
// it defaults to the directory "onionkeys" current working directory.
|
||||
// reference it by calling OnionKeystorePath() to check for errors
|
||||
var ONION_KEYSTORE_PATH = tordefault
|
||||
|
||||
// TLS_KEYSTORE_PATH is the place where TLS Keys will be saved.
|
||||
// it defaults to the directory "tlskeys" current working directory.
|
||||
// reference it by calling TLSKeystorePath() to check for errors
|
||||
var TLS_KEYSTORE_PATH = tlsdefault
|
||||
|
||||
// I2PKeystorePath returns the path to the I2P Keystore. If the
|
||||
// path is not set, it returns the default path. If the path does
|
||||
// not exist, it creates it.
|
||||
@ -79,6 +85,24 @@ func DeleteTorKeyStore() error {
|
||||
return os.RemoveAll(ONION_KEYSTORE_PATH)
|
||||
}
|
||||
|
||||
// TLSKeystorePath returns the path to the TLS Keystore. If the
|
||||
// path is not set, it returns the default path. If the path does
|
||||
// not exist, it creates it.
|
||||
func TLSKeystorePath() (string, error) {
|
||||
if _, err := os.Stat(TLS_KEYSTORE_PATH); err != nil {
|
||||
err := os.MkdirAll(TLS_KEYSTORE_PATH, 0755)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return TLS_KEYSTORE_PATH, nil
|
||||
}
|
||||
|
||||
// DeleteTLSKeyStore deletes the TLS Keystore.
|
||||
func DeleteTLSKeyStore() error {
|
||||
return os.RemoveAll(TLS_KEYSTORE_PATH)
|
||||
}
|
||||
|
||||
// Dial returns a connection for the given network and address.
|
||||
// network is ignored. If the address ends in i2p, it returns an I2P connection.
|
||||
// if the address ends in anything else, it returns a Tor connection.
|
||||
|
@ -2,10 +2,11 @@
|
||||
|
||||
GITHUB_USER=eyedeekay
|
||||
GITHUB_REPO=onramp
|
||||
GITHUB_NAME="Initial Release"
|
||||
GITHUB_DESCRIPTION=$(cat DESC.md)
|
||||
GITHUB_TAG=0.0.1
|
||||
|
||||
github-release --user "${GITHUB_USER}" \
|
||||
github-release release --user "${GITHUB_USER}" \
|
||||
--repo "${GITHUB_REPO}" \
|
||||
--name "${GITHUB_NAME}" \
|
||||
--description "${GITHUB_DESCRIPTION}" \
|
||||
|
176
tls.go
Normal file
176
tls.go
Normal file
@ -0,0 +1,176 @@
|
||||
package onramp
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CreateTLSCertificate generates a TLS certificate for the given hostname,
|
||||
// and stores it in the TLS keystore for the application.
|
||||
func CreateTLSCertificate(tlsHost string) error {
|
||||
tlsCert := tlsHost + ".crt"
|
||||
tlsKey := tlsHost + ".pem"
|
||||
_, certErr := os.Stat(tlsCert)
|
||||
_, keyErr := os.Stat(tlsKey)
|
||||
if certErr != nil || keyErr != nil {
|
||||
if certErr != nil {
|
||||
fmt.Printf("Unable to read TLS certificate '%s'\n", tlsCert)
|
||||
}
|
||||
if keyErr != nil {
|
||||
fmt.Printf("Unable to read TLS key '%s'\n", tlsKey)
|
||||
}
|
||||
|
||||
if err := createTLSCertificate(tlsHost); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createTLSCertificate(host string) error {
|
||||
fmt.Println("Generating TLS keys. This may take a minute...")
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tlsCert, err := NewTLSCertificate(host, priv)
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
privStore, err := TLSKeystorePath()
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
|
||||
certFile := filepath.Join(privStore, host+".crt")
|
||||
// save the TLS certificate
|
||||
certOut, err := os.Create(certFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open %s for writing: %s", host+".crt", err)
|
||||
}
|
||||
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: tlsCert})
|
||||
certOut.Close()
|
||||
fmt.Printf("\tTLS certificate saved to: %s\n", host+".crt")
|
||||
|
||||
// save the TLS private key
|
||||
privFile := filepath.Join(privStore, host+".pem")
|
||||
keyOut, err := os.OpenFile(privFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open %s for writing: %v", privFile, err)
|
||||
}
|
||||
secp384r1, err := asn1.Marshal(asn1.ObjectIdentifier{1, 3, 132, 0, 34}) // http://www.ietf.org/rfc/rfc5480.txt
|
||||
pem.Encode(keyOut, &pem.Block{Type: "EC PARAMETERS", Bytes: secp384r1})
|
||||
ecder, err := x509.MarshalECPrivateKey(priv)
|
||||
pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: ecder})
|
||||
pem.Encode(keyOut, &pem.Block{Type: "CERTIFICATE", Bytes: tlsCert})
|
||||
|
||||
keyOut.Close()
|
||||
fmt.Printf("\tTLS private key saved to: %s\n", privFile)
|
||||
|
||||
// CRL
|
||||
crlFile := filepath.Join(privStore, host+".crl")
|
||||
crlOut, err := os.OpenFile(crlFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open %s for writing: %s", crlFile, err)
|
||||
}
|
||||
crlcert, err := x509.ParseCertificate(tlsCert)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Certificate with unknown critical extension was not parsed: %s", err)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
revokedCerts := []pkix.RevokedCertificate{
|
||||
{
|
||||
SerialNumber: crlcert.SerialNumber,
|
||||
RevocationTime: now,
|
||||
},
|
||||
}
|
||||
|
||||
crlBytes, err := crlcert.CreateCRL(rand.Reader, priv, revokedCerts, now, now)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating CRL: %s", err)
|
||||
}
|
||||
_, err = x509.ParseDERCRL(crlBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reparsing CRL: %s", err)
|
||||
}
|
||||
pem.Encode(crlOut, &pem.Block{Type: "X509 CRL", Bytes: crlBytes})
|
||||
crlOut.Close()
|
||||
fmt.Printf("\tTLS CRL saved to: %s\n", crlFile)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewTLSCertificate generates a new TLS certificate for the given hostname,
|
||||
// returning it as bytes.
|
||||
func NewTLSCertificate(host string, priv *ecdsa.PrivateKey) ([]byte, error) {
|
||||
return NewTLSCertificateAltNames(priv, host)
|
||||
}
|
||||
|
||||
// NewTLSCertificateAltNames generates a new TLS certificate for the given hostname,
|
||||
// and a list of alternate names, returning it as bytes.
|
||||
func NewTLSCertificateAltNames(priv *ecdsa.PrivateKey, hosts ...string) ([]byte, error) {
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(5 * 365 * 24 * time.Hour)
|
||||
host := ""
|
||||
if len(hosts) > 0 {
|
||||
host = hosts[0]
|
||||
}
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"I2P Anonymous Network"},
|
||||
OrganizationalUnit: []string{"I2P"},
|
||||
Locality: []string{"XX"},
|
||||
StreetAddress: []string{"XX"},
|
||||
Country: []string{"XX"},
|
||||
CommonName: host,
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
SignatureAlgorithm: x509.ECDSAWithSHA512,
|
||||
|
||||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
DNSNames: hosts[1:],
|
||||
}
|
||||
|
||||
hosts = append(hosts, strings.Split(host, ",")...)
|
||||
for _, h := range hosts {
|
||||
if ip := net.ParseIP(h); ip != nil {
|
||||
template.IPAddresses = append(template.IPAddresses, ip)
|
||||
} else {
|
||||
template.DNSNames = append(template.DNSNames, h)
|
||||
}
|
||||
}
|
||||
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return derBytes, nil
|
||||
}
|
Reference in New Issue
Block a user