package cm509 import ( "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/base64" "encoding/pem" "fmt" "math/big" "net" "time" ) type CreateIssuerPairParams struct { CommonName string SignerCert string SignerKey string } type CreateIssuerPairResult struct { Name string Cert string Key string } func CreateIssuerPair(params *CreateIssuerPairParams) (*CreateIssuerPairResult, error) { var err error res := &CreateIssuerPairResult{} if params.SignerKey != "" && params.SignerCert == "" { err = fmt.Errorf("The signature key and certificate must be defined together") return res, err } if params.SignerKey == "" && params.SignerCert != "" { err = fmt.Errorf("The signature key and certificate must be defined together") return res, err } var signerKey any if params.SignerKey != "" { signerKey, err = ParseDoubleEncodedKey(params.SignerKey) if err != nil { return res, err } } var signerCert *x509.Certificate if params.SignerCert != "" { signerCert, err = ParseDoubleEncodedCerificate(params.SignerCert) if err != nil { return res, err } } certPem := make([]byte, 0) keyPem := make([]byte, 0) now := time.Now() const yearsAfter int = 10 const keySize int = 2048 certKey, err := rsa.GenerateKey(rand.Reader, keySize) if err != nil { err := fmt.Errorf("Can't create a private key: %v", err) return res, err } keyPemBlock := &pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(certKey), } keyPem = pem.EncodeToMemory(keyPemBlock) certSubject := pkix.Name{ CommonName: params.CommonName, } certIssuer := certSubject if signerCert != nil { certIssuer = signerCert.Subject } var issuerKey any = certKey if signerKey != nil { issuerKey = signerKey } res.Name = certSubject.String() certTempl := &x509.Certificate{ SerialNumber: big.NewInt(now.Unix()), NotBefore: now, NotAfter: now.AddDate(yearsAfter, 0, 0), Subject: certSubject, Issuer: certIssuer, IsCA: true, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageCRLSign, BasicConstraintsValid: true, } parentCert := certTempl if signerCert != nil { parentCert = signerCert } certBytes, err := x509.CreateCertificate(rand.Reader, certTempl, parentCert, &certKey.PublicKey, issuerKey) if err != nil { err := fmt.Errorf("Can't create a certificate: %v", err) return res, err } certPemBlock := pem.Block{ Type: "CERTIFICATE", Bytes: certBytes, } certPem = pem.EncodeToMemory(&certPemBlock) if err != nil { return res, err } res.Cert = base64.StdEncoding.EncodeToString(certPem) res.Key = base64.StdEncoding.EncodeToString(keyPem) return res, err } type CreateServicePairParams struct { CommonName string DNSNames []string IPAddresses []string IssuerKey string IssuerCert string } type CreateServicePairResult struct { Name string Cert string Key string } func CreateServicePair(params *CreateServicePairParams) (*CreateServicePairResult, error) { var err error res := &CreateServicePairResult{} if params.IssuerKey != "" && params.IssuerCert == "" { err = fmt.Errorf("The signature key and certificate must be defined together") return res, err } if params.IssuerKey == "" && params.IssuerCert != "" { err = fmt.Errorf("The signature key and certificate must be defined together") return res, err } var signerKey any if params.IssuerKey != "" { signerKey, err = ParseDoubleEncodedKey(params.IssuerKey) if err != nil { return res, err } } var signerCert *x509.Certificate if params.IssuerCert != "" { signerCert, err = ParseDoubleEncodedCerificate(params.IssuerCert) if err != nil { return res, err } } certPem := make([]byte, 0) keyPem := make([]byte, 0) now := time.Now() const yearsAfter int = 10 const keySize int = 2048 certKey, err := rsa.GenerateKey(rand.Reader, keySize) if err != nil { err := fmt.Errorf("Can't create a private key: %v", err) return res, err } keyPemBlock := &pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(certKey), } keyPem = pem.EncodeToMemory(keyPemBlock) certSubject := pkix.Name{ CommonName: params.CommonName, } certIssuer := certSubject if signerCert != nil { certIssuer = signerCert.Subject } var issuerKey any = certKey if signerKey != nil { issuerKey = signerKey } res.Name = certSubject.String() netAddresses := make([]net.IP, 0) for _, ipAddress := range params.IPAddresses { netAddress := net.ParseIP(ipAddress) netAddresses = append(netAddresses, netAddress) } certTempl := &x509.Certificate{ SerialNumber: big.NewInt(now.Unix()), NotBefore: now, NotAfter: now.AddDate(yearsAfter, 0, 0), Subject: certSubject, Issuer: certIssuer, DNSNames: params.DNSNames, IPAddresses: netAddresses, IsCA: false, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, KeyUsage: x509.KeyUsageDigitalSignature, BasicConstraintsValid: true, } parentCert := certTempl if signerCert != nil { parentCert = signerCert } certBytes, err := x509.CreateCertificate(rand.Reader, certTempl, parentCert, &certKey.PublicKey, issuerKey) if err != nil { err := fmt.Errorf("Can't create a certificate: %v", err) return res, err } certPemBlock := pem.Block{ Type: "CERTIFICATE", Bytes: certBytes, } certPem = pem.EncodeToMemory(&certPemBlock) if err != nil { return res, err } res.Cert = base64.StdEncoding.EncodeToString(certPem) res.Key = base64.StdEncoding.EncodeToString(keyPem) return res, err } func XXXCreateServicePair(params *CreateServicePairParams) (*CreateServicePairResult, error) { var err error res := &CreateServicePairResult{} certPem := make([]byte, 0) keyPem := make([]byte, 0) now := time.Now() const yearsAfter int = 10 const keySize int = 2048 key, err := rsa.GenerateKey(rand.Reader, keySize) if err != nil { err := fmt.Errorf("Can't create a private key: %v", err) return res, err } keyPemBlock := pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key), } keyPem = pem.EncodeToMemory(&keyPemBlock) caKeyPem, err := base64.StdEncoding.DecodeString(params.IssuerKey) if err != nil { return res, err } pemBlock, _ := pem.Decode(caKeyPem) if pemBlock == nil { err := fmt.Errorf("Can't parse a CA private key block") return res, err } caKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes) if err != nil { err := fmt.Errorf("Can't parse a CA private key") return res, err } netAddresses := make([]net.IP, 0) for _, ipAddress := range params.IPAddresses { netAddress := net.ParseIP(ipAddress) netAddresses = append(netAddresses, netAddress) } certTempl := x509.Certificate{ SerialNumber: big.NewInt(now.Unix()), NotBefore: now, NotAfter: now.AddDate(yearsAfter, 0, 0), Subject: pkix.Name{ CommonName: params.CommonName, }, DNSNames: params.DNSNames, IPAddresses: netAddresses, IsCA: false, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, KeyUsage: x509.KeyUsageDigitalSignature, BasicConstraintsValid: true, } certBytes, err := x509.CreateCertificate(rand.Reader, &certTempl, &certTempl, &key.PublicKey, caKey) if err != nil { return res, fmt.Errorf("Can't create a certificate: %v", err) } certPemBlock := pem.Block{ Type: "CERTIFICATE", Bytes: certBytes, } certPem = pem.EncodeToMemory(&certPemBlock) if err != nil { return res, err } res.Cert = base64.StdEncoding.EncodeToString(certPem) res.Key = base64.StdEncoding.EncodeToString(keyPem) return res, err } func ParseDoubleEncodedCerificate(certString string) (*x509.Certificate, error) { var err error res := &x509.Certificate{} certPEM, err := base64.StdEncoding.DecodeString(certString) if err != nil { err := fmt.Errorf("Failed to parse base64 certificate string: %v", err) return res, err } certBlock, _ := pem.Decode([]byte(certPEM)) if certBlock == nil { err := fmt.Errorf("Failed to parse certificate PEM") return res, err } if certBlock.Type != "CERTIFICATE" { err := fmt.Errorf("Unknown PEM certificate type: %s", certBlock.Type) return res, err } if len(certBlock.Bytes) == 0 { err := fmt.Errorf("Empty PEM certificate block") return res, err } res, err = x509.ParseCertificate(certBlock.Bytes) if err != nil { return res, err } return res, err } func ParseEncodedCerificate(certPEM string) (*x509.Certificate, error) { var err error res := &x509.Certificate{} certBlock, _ := pem.Decode([]byte(certPEM)) if certBlock == nil { err := fmt.Errorf("Failed to parse certificate PEM") return res, err } if certBlock.Type != "CERTIFICATE" { err := fmt.Errorf("Unknown PEM certificate type: %s", certBlock.Type) return res, err } if len(certBlock.Bytes) == 0 { err := fmt.Errorf("Empty PEM certificate block") return res, err } res, err = x509.ParseCertificate(certBlock.Bytes) if err != nil { return res, err } return res, err } func ParseDoubleEncodedKey(keyString string) (any, error) { var err error var res any keyPEM, err := base64.StdEncoding.DecodeString(keyString) if err != nil { err := fmt.Errorf("Failed to parse base64 key string: %v", err) return res, err } keyBlock, _ := pem.Decode([]byte(keyPEM)) if keyBlock == nil { err := fmt.Errorf("Failed to parse key PEM") return res, err } switch keyBlock.Type { case "PRIVATE KEY": res, err = x509.ParsePKCS8PrivateKey(keyBlock.Bytes) if err != nil { return res, err } case "RSA PRIVATE KEY": res, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes) if err != nil { return res, err } case "EC PRIVATE KEY": res, err = x509.ParseECPrivateKey(keyBlock.Bytes) if err != nil { return res, err } default: err := fmt.Errorf("Unknown PEM key type: %s", keyBlock.Type) return res, err } return res, err } func ParseEncodedKey(keyPEM string) (any, error) { var err error var res any keyBlock, _ := pem.Decode([]byte(keyPEM)) if keyBlock == nil { err := fmt.Errorf("Failed to parse key PEM") return res, err } switch keyBlock.Type { case "PRIVATE KEY": res, err = x509.ParsePKCS8PrivateKey(keyBlock.Bytes) if err != nil { return res, err } case "RSA PRIVATE KEY": res, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes) if err != nil { return res, err } case "EC PRIVATE KEY": res, err = x509.ParseECPrivateKey(keyBlock.Bytes) if err != nil { return res, err } default: err := fmt.Errorf("Unknown PEM key type: %s", keyBlock.Type) return res, err } return res, err } func CheckDoubleEncodedCertificateChain(topIssuerCN string, certStrings []string) ([]string, error) { var err error res := make([]string, 0) certObjs := make([]*x509.Certificate, 0) for _, certString := range certStrings { certObj, err := ParseDoubleEncodedCerificate(certString) if err != nil { return res, err } certObjs = append(certObjs, certObj) } issuerFound := false issuerIndex := -1 for i, certObj := range certObjs { if topIssuerCN == certObj.Subject.String() { issuerIndex = i issuerFound = true } } if !issuerFound { err := fmt.Errorf("Issuer for %s cannot found", topIssuerCN) return res, err } interCertObj := certObjs[issuerIndex] interCertString := certStrings[issuerIndex] if !interCertObj.IsCA { err := fmt.Errorf("Issuer %s is not CA", interCertObj.Subject.String()) return res, err } expired := interCertObj.NotAfter.Before(time.Now()) if !expired { err := fmt.Errorf("Issuer %s expired %v", interCertObj.Subject.String(), interCertObj.NotAfter) return res, err } res = append(res, interCertString) if interCertObj.Subject.String() == interCertObj.Issuer.String() { return res, err } updatedCertStrings := append(certStrings[:issuerIndex], certStrings[issuerIndex+1:]...) topIssuerCN = interCertObj.Issuer.String() certStringsTail, err := CheckDoubleEncodedCertificateChain(topIssuerCN, updatedCertStrings) if err != nil { return res, err } res = append(res, certStringsTail...) return res, err }