package certs import ( "bytes" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "fmt" "math/big" "time" "gitea.benny.dog/torjus/ezshare/store" ) type CertService struct { caCert *x509.Certificate caKey crypto.Signer store store.CertificateStore } func NewCertService(s store.CertificateStore, certBytes, keyBytes []byte) (*CertService, error) { // Try to decode key as PEM keyBlock, _ := pem.Decode(keyBytes) if keyBlock != nil { if keyBlock.Type != "EC PRIVATE KEY" { return nil, fmt.Errorf("private key is not of type EC PRIVATE KEY: %s", keyBlock.Type) } keyBytes = keyBlock.Bytes } // Try to decode cert as PEM certBlock, _ := pem.Decode(certBytes) if certBlock != nil { if certBlock.Type != "CERTIFICATE" { return nil, fmt.Errorf("certificate is not of type CERTIFICATE: %s", certBlock.Type) } certBytes = certBlock.Bytes } caCert, err := x509.ParseCertificate(certBytes) if err != nil { return nil, fmt.Errorf("unable to parse certificate: %w", err) } if !caCert.IsCA { return nil, fmt.Errorf("certificate is not CA") } caKey, err := x509.ParseECPrivateKey(keyBytes) if err != nil { return nil, fmt.Errorf("unable to parse private key: %w", err) } return &CertService{caCert: caCert, caKey: caKey, store: s}, nil } func (cs *CertService) NewClient(id string) ([]byte, []byte, error) { cert := &x509.Certificate{ SerialNumber: big.NewInt(time.Now().UnixMilli()), Subject: pkix.Name{ CommonName: id, Organization: []string{"ezshare"}, Country: []string{"No"}, Locality: []string{"Oslo"}, }, NotBefore: time.Now(), NotAfter: time.Now().AddDate(0, 0, 30), ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, KeyUsage: x509.KeyUsageDigitalSignature, } certPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, nil, err } certPrivKeyBytes, err := x509.MarshalECPrivateKey(certPrivKey) if err != nil { return nil, nil, err } certBytes, err := x509.CreateCertificate(rand.Reader, cert, cs.caCert, &certPrivKey.PublicKey, cs.caKey) if err != nil { return nil, nil, err } keyPEM := new(bytes.Buffer) if err := pem.Encode(keyPEM, &pem.Block{ Type: "EC PRIVATE KEY", Bytes: certPrivKeyBytes, }); err != nil { return nil, nil, fmt.Errorf("unable to encode client private key: %w", err) } certPEM := new(bytes.Buffer) if err := pem.Encode(certPEM, &pem.Block{ Type: "CERTIFICATE", Bytes: certBytes, }); err != nil { return nil, nil, fmt.Errorf("unable to encode client private key: %w", err) } signed, err := x509.ParseCertificate(certBytes) if err != nil { return nil, nil, err } if err := cs.store.StoreCertificate(signed); err != nil { return nil, nil, err } return certPEM.Bytes(), keyPEM.Bytes(), nil } func (cs *CertService) VerifyClient(certBytes []byte) (string, error) { // Try to decode cert as PEM certBlock, _ := pem.Decode(certBytes) if certBlock != nil { if certBlock.Type != "CERTIFICATE" { return "", fmt.Errorf("certificate is not of type CERTIFICATE: %s", certBlock.Type) } certBytes = certBlock.Bytes } cert, err := x509.ParseCertificate(certBytes) if err != nil { return "", fmt.Errorf("unable to parse certificate: %w", err) } rootPool := x509.NewCertPool() rootPool.AddCert(cs.caCert) if _, err := cert.Verify(x509.VerifyOptions{ Roots: rootPool, }); err != nil { return "", fmt.Errorf("unable to verify: %w", err) } revoked, err := cs.store.IsRevoked(cert.SerialNumber.String()) if err != nil { return "", fmt.Errorf("unable to check if revoked: %w", err) } if revoked { return "", fmt.Errorf("certificate is revoked") } return cert.Subject.CommonName, nil }