@ -2,6 +2,7 @@ package handlers
import (
"bytes"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"errors"
@ -11,7 +12,6 @@ import (
"os/exec"
"time"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
)
@ -29,20 +29,58 @@ func NewSigningRequestRegistry(caCertificates []*x509.Certificate) *SigningReque
}
}
type SigningRequestAttributes struct {
CommonName string
CSRBytes [ ] byte
RequestToken string
}
func ( registry * SigningRequestRegistry ) AddSigningRequest ( request * requestData ) ( string , error ) {
requestUuid , err := uuid . NewRandom ( )
request Token, csrBytes , err := validateCsr ( request . Csr )
if err != nil {
return "" , err
}
requestAttributes := & SigningRequestAttributes {
CommonName : request . CommonName ,
CSRBytes : csrBytes ,
RequestToken : requestToken ,
}
go func ( ) {
responseChannel := make ( chan * responseData , 1 )
registry . requests [ requestUuid . String ( ) ] = responseChannel
registry . signCertificate ( responseChannel , request )
registry . requests [ request Token ] = responseChannel
registry . signCertificate ( responseChannel , request Attributes )
} ( )
return request Uuid. String ( ) , nil
return request Token , nil
}
func ( registry * SigningRequestRegistry ) signCertificate ( channel chan * responseData , request * requestData ) {
func validateCsr ( csr string ) ( string , [ ] byte , error ) {
csrBlock , _ := pem . Decode ( [ ] byte ( csr ) )
if csrBlock == nil {
return "" , nil , errors . New ( "request data did not contain valid PEM data" )
}
if csrBlock . Type != "CERTIFICATE REQUEST" {
return "" , nil , fmt . Errorf ( "request is not valid, type in PEM data is %s" , csrBlock . Type )
}
var err error
var csrContent * x509 . CertificateRequest
csrContent , err = x509 . ParseCertificateRequest ( csrBlock . Bytes )
if err != nil {
return "" , nil , err
}
if err = csrContent . CheckSignature ( ) ; err != nil {
log . Errorf ( "invalid CSR signature %v" , err )
return "" , nil , err
}
// generate request token as defined in CAB Baseline Requirements 1.7.3 Request Token definition
requestToken := fmt . Sprintf (
"%s%x" , time . Now ( ) . UTC ( ) . Format ( "200601021504" ) , sha256 . Sum256 ( csrContent . Raw ) ,
)
log . Debugf ( "generated request token %s" , requestToken )
return requestToken , csrContent . Raw , nil
}
func ( registry * SigningRequestRegistry ) signCertificate ( channel chan * responseData , request * SigningRequestAttributes ) {
responseData , err := registry . sign ( request )
if err != nil {
log . Error ( err )
@ -52,21 +90,26 @@ func (registry *SigningRequestRegistry) signCertificate(channel chan *responseDa
channel <- responseData
}
func ( registry * SigningRequestRegistry ) sign ( request * requestData) ( response * responseData , err error ) {
log . Debugf( "received CSR for %s:\n\n%s" , request . CommonName , request . Csr )
func ( registry * SigningRequestRegistry ) sign ( request * SigningRequestAttributes) ( * responseData , error ) {
log . Infof( "handling signing request %s" , request . RequestToken )
subjectDN := fmt . Sprintf ( "/CN=%s" , request . CommonName )
var err error
var csrFile * os . File
if csrFile , err = ioutil . TempFile ( "" , "*.csr.pem" ) ; err != nil {
log . Errorf ( "could not open temporary file: %s" , err )
return
return nil , err
}
if _ , err = csrFile . Write ( [ ] byte ( request . Csr ) ) ; err != nil {
if err = pem . Encode ( csrFile , & pem . Block {
Type : "CERTIFICATE REQUEST" ,
Bytes : request . CSRBytes ,
} ) ; err != nil {
log . Errorf ( "could not write CSR to file: %s" , err )
return
return nil , err
}
if err = csrFile . Close ( ) ; err != nil {
log . Errorf ( "could not close CSR file: %s" , err )
return
return nil , err
}
defer func ( file * os . File ) {
err = os . Remove ( file . Name ( ) )
@ -81,7 +124,7 @@ func (registry *SigningRequestRegistry) sign(request *requestData) (response *re
opensslCommand := exec . Command (
"openssl" , "ca" , "-config" , "ca.cnf" ,
"-policy" , "policy_match" , "-extensions" , "client_ext" ,
"-batch" , "-subj" , subjectDN , "-utf8" , "-rand_serial" , "-in" , "in.pem" )
"-batch" , "-subj" , subjectDN , "-utf8" , "-rand_serial" , "-in" , csrFile . Name ( ) )
var out , cmdErr bytes . Buffer
opensslCommand . Stdout = & out
opensslCommand . Stderr = & cmdErr
@ -89,32 +132,32 @@ func (registry *SigningRequestRegistry) sign(request *requestData) (response *re
if err != nil {
log . Error ( err )
log . Error ( cmdErr . String ( ) )
return
return nil , err
}
var block * pem . Block
if block , _ = pem . Decode ( out . Bytes ( ) ) ; block == nil {
err = fmt . Errorf ( "could not decode pem" )
return
return nil , err
}
var certificate * x509 . Certificate
if certificate , err = x509 . ParseCertificate ( block . Bytes ) ; err != nil {
return
return nil , err
}
var caChain [ ] string
if caChain , err = registry . getCAChain ( certificate ) ; err != nil {
return
return nil , err
}
response = & responseData {
response : = & responseData {
Certificate : string ( pem . EncodeToMemory ( & pem . Block {
Type : "CERTIFICATE" ,
Bytes : certificate . Raw ,
} ) ) ,
CAChain : caChain ,
}
return
return response , nil
}
func ( registry * SigningRequestRegistry ) GetResponseChannel ( requestUuid string ) ( chan * responseData , error ) {