/* 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 return generateCertificateResponse, nil } 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) if err != nil { return nil, err } 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 } template := x509.Certificate{ 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) if err != nil { return nil, err } 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 }