Skip to content
Snippets Groups Projects
generate_certificate.go 5.04 KiB
Newer Older
/*
Copyright (c) 2018 Vereign AG [https://www.vereign.com]

This is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package handler

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"io/ioutil"
	"math/big"
	"time"

	"code.vereign.com/code/viam-apis/data-storage-agent/client"
	"code.vereign.com/code/viam-apis/key-storage-agent/api"
	"code.vereign.com/code/viam-apis/utils"
	"code.vereign.com/code/viam-apis/versions"
	"golang.org/x/net/context"
)

func (s *KeyStorageServerImpl) GenerateCertificate(ctx context.Context, in *api.GenerateCertificateRequest) (*api.GenerateCertificateResponse, error) {
	auth := s.CreateAuthentication(ctx)

	client := &client.DataStorageClientImpl{}
	client.SetUpClient(auth, s.DataStorageUrl, s.CertFilePath)
	defer client.CloseClient()

	generateCertificateResponse := &api.GenerateCertificateResponse{}

	publicKeyMessage, statusList := getKey(client, in.Uuid, api.KeyType_PUBLIC)
	if statusList != nil {
		generateCertificateResponse.StatusList = statusList
	certificateBytes, err := generateCertificate(publicKeyMessage.Content, s.VereignCertFilePath,
		s.VereignPrivateKeyFilePath, in.CertificateData)
	if err != nil {
		generateCertificateResponse.StatusList = utils.AddStatus(generateCertificateResponse.StatusList,
			"400", api.StatusType_ERROR, err.Error())
		return generateCertificateResponse, nil
	}

	certificateMessage := &api.Key{
		Content: certificateBytes,
	}

	result, errors, err := client.DoPutDataCall("keys", in.Uuid+"/"+api.KeyType.String(api.KeyType_CERTIFICATE), certificateMessage, versions.EntitiesManagementAgentApiVersion)
	generateCertificateResponse.StatusList = handlePutDataErrors(generateCertificateResponse.StatusList, errors, err)

	if generateCertificateResponse.StatusList == nil || len(generateCertificateResponse.StatusList) == 0 {
		generateCertificateResponse.StatusList = utils.AddStatus(generateCertificateResponse.StatusList,
			"200", api.StatusType_INFO, result)
	}

	return generateCertificateResponse, nil
}

func generateCertificate(publicKeyBytes []byte, caCertFilePath string, caPrivateKeyFilePath string,
	certificateData *api.CertificateData) ([]byte, error) {
	publicKeyPemBlock, _ := pem.Decode(publicKeyBytes)

	publicKey, err := x509.ParsePKIXPublicKey(publicKeyPemBlock.Bytes)
	notBeforeTime := time.Unix(certificateData.NotBefore/1000, 0).UTC()
	notAfterTime := time.Unix(certificateData.NotAfter/1000, 0).UTC()

	max := new(big.Int)
	max.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(max, big.NewInt(1))

	//Generate cryptographically strong pseudo-random between 0 - max
	sn, err := rand.Int(rand.Reader, max)

	if err != nil {
		return nil, err
	}
		SerialNumber: sn,
		Subject: pkix.Name{
			Country:            []string{certificateData.Country},
			Organization:       []string{certificateData.Organization},
			OrganizationalUnit: []string{certificateData.OrganizationalUnit},
			CommonName:         certificateData.CommonName,
		},
		NotBefore:             notBeforeTime,
		NotAfter:              notAfterTime,
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
		IsCA:     false,
		DNSNames: []string{certificateData.Host},
	}

	caCertificate, err := readCertificateFromFile(caCertFilePath)
	if err != nil {
		return nil, err
	}
	caPrivateKey, err := readPrivateKeyFromFile(caPrivateKeyFilePath)
	if err != nil {
		return nil, err
	}
	certificateBytes, err := x509.CreateCertificate(rand.Reader, &template, caCertificate, publicKey, caPrivateKey)
	certificatePemBlock := &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: certificateBytes,
	}

	certificatePemBytes := pem.EncodeToMemory(certificatePemBlock)

	return certificatePemBytes, nil
}

func readPrivateKeyFromFile(fileName string) (*rsa.PrivateKey, error) {
	privateKeyPemBlock, err := readPemBlockFromFile(fileName)
	if err != nil {
		return nil, err
	}

	parseResult, err := x509.ParsePKCS8PrivateKey(privateKeyPemBlock.Bytes)
	if err != nil {
		return nil, err
	}
	privateKey := parseResult.(*rsa.PrivateKey)

	return privateKey, nil
}

func readPemBlockFromFile(fileName string) (*pem.Block, error) {
	fileBytes, err := ioutil.ReadFile(fileName)
	if err != nil {
		return nil, err
	}

	certificatePemBlock, _ := pem.Decode(fileBytes)

	return certificatePemBlock, nil
}