Browse Source

Implemented TLS client auth.

pull/28/head
Simon Eisenmann 11 years ago
parent
commit
a6a930f73a
  1. 21
      src/app/spreed-speakfreely-server/main.go
  2. 110
      src/app/spreed-speakfreely-server/tls.go
  3. 72
      src/app/spreed-speakfreely-server/users.go

21
src/app/spreed-speakfreely-server/main.go

@ -24,6 +24,8 @@ package main @@ -24,6 +24,8 @@ package main
import (
"app/spreed-speakfreely-server/sleepy"
"bytes"
"crypto/tls"
"crypto/x509"
"flag"
"fmt"
"github.com/gorilla/mux"
@ -394,6 +396,25 @@ func runner(runtime phoenix.Runtime) error { @@ -394,6 +396,25 @@ func runner(runtime phoenix.Runtime) error {
runtime.DefaultHTTPHandler(r)
runtime.DefaultHTTPSHandler(r)
if tlsConfig, err := runtime.TLSConfig(); err == nil {
tlsConfig.ClientAuth = tls.VerifyClientCertIfGiven
// Create cert pool.
pool := x509.NewCertPool()
if certificateFn, err := runtime.GetString("users", "certificate_certificate"); err == nil {
if certificate, err := loadX509Certificate(certificateFn); err == nil {
for _, derCert := range certificate.Certificate {
cert, err := x509.ParseCertificate(derCert)
if err != nil {
continue
}
pool.AddCert(cert)
}
}
log.Printf("Initialized TLS auth pool with %d certtificates.", len(pool.Subjects()))
}
tlsConfig.ClientCAs = pool
}
return runtime.Start()
}

110
src/app/spreed-speakfreely-server/tls.go

@ -0,0 +1,110 @@ @@ -0,0 +1,110 @@
/*
* TLS helpers for Go based on crypto/tls package.
*
* Copyright (C) 2014 struktur AG. All rights reserved.
* Copyright 2011 The Go Authors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package main
import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"io/ioutil"
"strings"
)
func loadX509PrivateKey(keyFile string) (privateKey crypto.PrivateKey, err error) {
keyPEMBlock, err := ioutil.ReadFile(keyFile)
if err != nil {
return
}
var keyDERBlock *pem.Block
for {
keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
if keyDERBlock == nil {
err = errors.New("failed to parse key PEM data")
return
}
if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
break
}
}
privateKey, err = parsePrivateKey(keyDERBlock.Bytes)
return
}
func loadX509Certificate(certFile string) (cert tls.Certificate, err error) {
certPEMBlock, err := ioutil.ReadFile(certFile)
if err != nil {
return
}
var certDERBlock *pem.Block
for {
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
if certDERBlock == nil {
break
}
if certDERBlock.Type == "CERTIFICATE" {
cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
}
}
if len(cert.Certificate) == 0 {
err = errors.New("failed to parse certificate PEM data")
}
return
}
// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
return key, nil
}
if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
switch key := key.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey:
return key, nil
default:
return nil, errors.New("found unknown private key type in PKCS#8 wrapping")
}
}
if key, err := x509.ParseECPrivateKey(der); err == nil {
return key, nil
}
return nil, errors.New("failed to parse private key")
}

72
src/app/spreed-speakfreely-server/users.go

@ -22,21 +22,19 @@ @@ -22,21 +22,19 @@
package main
import (
"crypto"
"crypto/hmac"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"github.com/longsleep/pkac"
"github.com/satori/go.uuid"
"github.com/strukturag/phoenix"
"io/ioutil"
"log"
"math/big"
"net/http"
@ -123,58 +121,12 @@ func (uh *UsersHTTPHeaderHandler) Create(un *UserNonce, request *http.Request) ( @@ -123,58 +121,12 @@ func (uh *UsersHTTPHeaderHandler) Create(un *UserNonce, request *http.Request) (
}
func loadPEMfromFile(fn string) (block *pem.Block, err error) {
data, err := ioutil.ReadFile(fn)
if err != nil {
return
}
block, _ = pem.Decode(data)
return block, nil
}
type UsersCertificateHandler struct {
validFor time.Duration
privateKey *rsa.PrivateKey
privateKey crypto.PrivateKey
certificate *x509.Certificate
}
func (uh *UsersCertificateHandler) loadPrivateKey(fn string) error {
pemBlock, err := loadPEMfromFile(fn)
if err != nil {
return err
}
privateKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
if err != nil {
return err
}
uh.privateKey = privateKey
log.Printf("Users certificate private key loaded from %s\n", fn)
return nil
}
func (uh *UsersCertificateHandler) loadCertificate(fn string) error {
pemBlock, err := loadPEMfromFile(fn)
if err != nil {
return err
}
certificates, err := x509.ParseCertificates(pemBlock.Bytes)
if err != nil {
return err
}
uh.certificate = certificates[0]
log.Printf("Users certificate loaded from %s\n", fn)
return nil
}
func (uh *UsersCertificateHandler) makeTemplate(commonName string) (*x509.Certificate, error) {
notBefore := time.Now()
@ -183,7 +135,7 @@ func (uh *UsersCertificateHandler) makeTemplate(commonName string) (*x509.Certif @@ -183,7 +135,7 @@ func (uh *UsersCertificateHandler) makeTemplate(commonName string) (*x509.Certif
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return nil, err
}
}
return &x509.Certificate{
SerialNumber: serialNumber,
@ -280,6 +232,7 @@ type Users struct { @@ -280,6 +232,7 @@ type Users struct {
func NewUsers(hub *Hub, realm string, runtime phoenix.Runtime) *Users {
var handler UsersHandler
var err error
mode, _ := runtime.GetString("users", "mode")
switch mode {
@ -300,10 +253,23 @@ func NewUsers(hub *Hub, realm string, runtime phoenix.Runtime) *Users { @@ -300,10 +253,23 @@ func NewUsers(hub *Hub, realm string, runtime phoenix.Runtime) *Users {
keyFn, _ := runtime.GetString("users", "certificate_key")
certificateFn, _ := runtime.GetString("users", "certificate_certificate")
if keyFn != "" && certificateFn != "" {
uh.loadPrivateKey(keyFn)
if uh.privateKey, err = loadX509PrivateKey(keyFn); err == nil {
log.Printf("Users certificate private key loaded from %s\n", keyFn)
} else {
log.Printf("Failed to load certificat private key: %s\n", err)
}
}
if certificateFn != "" {
uh.loadCertificate(certificateFn)
if certificate, err := loadX509Certificate(certificateFn); err == nil {
if certificates, err := x509.ParseCertificates(certificate.Certificate[0]); err == nil {
uh.certificate = certificates[0]
log.Printf("Users certificate loaded from %s\n", certificateFn)
} else {
log.Printf("Failed to parse users certificate: %s\n", err)
}
} else {
log.Printf("Failed to load users certificate: %s\n", err)
}
}
handler = uh
default:

Loading…
Cancel
Save