]> Cypherpunks.ru repositories - gogost.git/commitdiff
Example X.509 certificate issuing utility
authorSergey Matveev <stargrave@stargrave.org>
Mon, 20 Feb 2023 12:54:53 +0000 (15:54 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Mon, 20 Feb 2023 12:54:53 +0000 (15:54 +0300)
cmd/cer-selfsigned-example/main.go [new file with mode: 0644]
go.mod
go.sum

diff --git a/cmd/cer-selfsigned-example/main.go b/cmd/cer-selfsigned-example/main.go
new file mode 100644 (file)
index 0000000..f859f8d
--- /dev/null
@@ -0,0 +1,227 @@
+// GoGOST -- Pure Go GOST cryptographic functions library
+// Copyright (C) 2015-2023 Sergey Matveev <stargrave@stargrave.org>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+// Example X.509 certificate issuing utility.
+package main
+
+import (
+       "crypto/rand"
+       "crypto/x509"
+       "crypto/x509/pkix"
+       "encoding/pem"
+       "flag"
+       "io"
+       "log"
+       "math/big"
+       "os"
+       "time"
+
+       "crypto/go.cypherpunks.ru/gogost/v5/gost3410"
+       "crypto/go.cypherpunks.ru/gogost/v5/gost34112012256"
+)
+
+const (
+       PEMKey = "PRIVATE KEY"
+       PEMCer = "CERTIFICATE"
+)
+
+func loadKeypair(filename string) (cer *x509.Certificate, prv any, err error) {
+       var data []byte
+       data, err = os.ReadFile(filename)
+       if err != nil {
+               return
+       }
+       var block *pem.Block
+       for len(data) > 0 {
+               block, data = pem.Decode(data)
+               if block == nil {
+                       continue
+               }
+               switch block.Type {
+               case PEMCer:
+                       cer, err = x509.ParseCertificate(block.Bytes)
+               case PEMKey:
+                       prv, err = x509.ParsePKCS8PrivateKey(block.Bytes)
+               }
+               if err != nil {
+                       return
+               }
+       }
+       return
+}
+
+func main() {
+       ca := flag.Bool("ca", false, "Enable BasicConstraints.cA")
+       cn := flag.String("cn", "", "Subject's CommonName")
+       country := flag.String("country", "", "Subject's Country")
+       serial := flag.Int64("serial", -1, "Serial number")
+       ai := flag.String("ai", "", "Signing algorithm: {256[ABCD],512[ABC]}")
+       issueWith := flag.String("issue-with", "", "Path to PEM with CA to issue the child")
+       reuseKey := flag.String("reuse-key", "", "Path to PEM with the key to reuse")
+       outKey := flag.String("out-key", "", "Path to PEM with the resulting key")
+       onlyKey := flag.Bool("only-key", false, "Only generate the key")
+       outCer := flag.String("out-cert", "", "Path to PEM with the resulting certificate")
+       flag.Parse()
+       log.SetFlags(log.Lshortfile)
+
+       if *cn == "" {
+               log.Fatalln("no CommonName is set")
+       }
+       var curve *gost3410.Curve
+       var sigAlg x509.SignatureAlgorithm
+       switch *ai {
+       case "256A":
+               curve = gost3410.CurveIdtc26gost341012256paramSetA()
+               sigAlg = x509.GOST256
+       case "256B":
+               curve = gost3410.CurveIdtc26gost341012256paramSetB()
+               sigAlg = x509.GOST256
+       case "256C":
+               curve = gost3410.CurveIdtc26gost341012256paramSetC()
+               sigAlg = x509.GOST256
+       case "256D":
+               curve = gost3410.CurveIdtc26gost341012256paramSetD()
+               sigAlg = x509.GOST256
+       case "512A":
+               curve = gost3410.CurveIdtc26gost341012512paramSetA()
+               sigAlg = x509.GOST512
+       case "512B":
+               curve = gost3410.CurveIdtc26gost341012512paramSetB()
+               sigAlg = x509.GOST512
+       case "512C":
+               curve = gost3410.CurveIdtc26gost341012512paramSetC()
+               sigAlg = x509.GOST512
+       default:
+               log.Fatalln("unknown curve name")
+       }
+
+       var err error
+       var caCer *x509.Certificate
+       var caPrv any
+       if *issueWith != "" {
+               caCer, caPrv, err = loadKeypair(*issueWith)
+               if err != nil {
+                       log.Fatalln(err)
+               }
+               sigAlg = caCer.SignatureAlgorithm
+       }
+
+       var prv any
+       if *reuseKey == "" {
+               prvRaw := make([]byte, curve.PointSize())
+               if _, err := io.ReadFull(rand.Reader, prvRaw); err != nil {
+                       log.Fatalln(err)
+               }
+               prv, err = gost3410.NewPrivateKey(curve, prvRaw)
+               if err != nil {
+                       log.Fatalln(err)
+               }
+               data, err := x509.MarshalPKCS8PrivateKey(prv)
+               if err != nil {
+                       log.Fatalln(err)
+               }
+               data = pem.EncodeToMemory(&pem.Block{Type: PEMKey, Bytes: data})
+               if *outKey == "" {
+                       _, err = os.Stdout.Write(data)
+               } else {
+                       err = os.WriteFile(*outKey, data, 0o666)
+               }
+               if err != nil {
+                       log.Fatalln(err)
+               }
+               if *onlyKey {
+                       return
+               }
+       } else {
+               _, prv, err = loadKeypair(*reuseKey)
+               if err != nil {
+                       log.Fatalln(err)
+               }
+       }
+
+       notBefore := time.Now().UTC()
+       days := 365 * 24 * time.Hour
+       if *ca {
+               days *= 10
+       }
+       notAfter := notBefore.Add(days)
+
+       sn := big.NewInt(0)
+       if *serial == -1 {
+               data := make([]byte, 16, gost34112012256.Size)
+               if _, err = io.ReadFull(rand.Reader, data); err != nil {
+                       log.Fatalln(err)
+               }
+               hasher := gost34112012256.New()
+               if _, err = hasher.Write(data); err != nil {
+                       log.Fatalln(err)
+               }
+               data = hasher.Sum(data[:0])
+               sn = sn.SetBytes(data[:20])
+       } else {
+               sn = sn.SetInt64(*serial)
+       }
+
+       subj := pkix.Name{CommonName: *cn}
+       if *country != "" {
+               subj.Country = []string{*country}
+       }
+
+       pub, err := prv.(*gost3410.PrivateKey).PublicKey()
+       if err != nil {
+               log.Fatalln(err)
+       }
+       hasher := gost34112012256.New()
+       if _, err = hasher.Write(pub.Raw()); err != nil {
+               log.Fatalln(err)
+       }
+       spki := hasher.Sum(nil)
+       spki = spki[:20]
+
+       cerTmpl := x509.Certificate{
+               KeyUsage:           x509.KeyUsageDigitalSignature,
+               NotBefore:          notBefore,
+               NotAfter:           notAfter,
+               SerialNumber:       sn,
+               SignatureAlgorithm: sigAlg,
+               Subject:            subj,
+               SubjectKeyId:       spki,
+       }
+       if *ca {
+               cerTmpl.IsCA = true
+               cerTmpl.KeyUsage |= x509.KeyUsageCertSign
+       } else {
+               cerTmpl.DNSNames = []string{*cn}
+       }
+
+       if caCer == nil {
+               caCer = &cerTmpl
+               caPrv = prv
+       }
+       data, err := x509.CreateCertificate(
+               rand.Reader,
+               &cerTmpl, caCer, pub,
+               &gost3410.PrivateKeyReverseDigest{Prv: caPrv.(*gost3410.PrivateKey)},
+       )
+       data = pem.EncodeToMemory(&pem.Block{Type: PEMCer, Bytes: data})
+       if *outCer == "" {
+               _, err = os.Stdout.Write(data)
+       } else {
+               err = os.WriteFile(*outCer, data, 0o666)
+       }
+       if err != nil {
+               log.Fatalln(err)
+       }
+}
diff --git a/go.mod b/go.mod
index ab71e19ecfdb7571ff4c24831e134b54f98f1017..377d625d2a8dd04640bbace235d0bc06477c7355 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -1,5 +1,5 @@
 module go.cypherpunks.ru/gogost/v5
 
-go 1.17
+go 1.18
 
-require golang.org/x/crypto v0.1.0
+require golang.org/x/crypto v0.6.0
diff --git a/go.sum b/go.sum
index 37c516a9ce556af92283972af3e907f080542f09..cdcbe20672504fba1d2616d34434146b0512b788 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -1,2 +1,2 @@
-golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
-golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
+golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
+golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=