vendor all dependencies
This commit is contained in:
parent
31b081c06e
commit
07a71d7b77
|
@ -0,0 +1,5 @@
|
||||||
|
module git.zero-knowledge.org/gibheer/pkictl
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require git.zero-knowledge.org/gibheer/pki v0.0.0-20220815203810-d34a8ab26ec7
|
|
@ -0,0 +1,2 @@
|
||||||
|
git.zero-knowledge.org/gibheer/pki v0.0.0-20220815203810-d34a8ab26ec7 h1:SFuERGJkxqBaWO+eNl4OuTsMNOoQjsC9OWKnTbOUkD4=
|
||||||
|
git.zero-knowledge.org/gibheer/pki v0.0.0-20220815203810-d34a8ab26ec7/go.mod h1:N0SOjUNlgYkDYBadg/Q5NjyA0Ee2w0rLf8bfsX3kzyE=
|
|
@ -0,0 +1,3 @@
|
||||||
|
This is the list of contributors to this project:
|
||||||
|
|
||||||
|
* Stefan Radomski
|
|
@ -0,0 +1,13 @@
|
||||||
|
Copyright (c) 2014, the authors of pkictl <gibheer@zero-knowledge.org>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@ -0,0 +1,7 @@
|
||||||
|
pki
|
||||||
|
===
|
||||||
|
|
||||||
|
This is a small library to make building private keys, public keys, signatures
|
||||||
|
and most of the certificate stuff a bit easier.
|
||||||
|
|
||||||
|
For a cli you can take a look at [pkictl](https://git.zero-knowledge.org/gibheer/pkictl)
|
|
@ -0,0 +1,181 @@
|
||||||
|
package pki
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// labels used in the pem file format to mark certificate sign requests and certificates
|
||||||
|
const (
|
||||||
|
PemLabelCertificateRequest = "CERTIFICATE REQUEST"
|
||||||
|
PemLabelCertificate = "CERTIFICATE"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Use CertificateData to fill in the minimum data you need to create a certificate
|
||||||
|
// sign request.
|
||||||
|
CertificateData struct {
|
||||||
|
Subject pkix.Name
|
||||||
|
|
||||||
|
DNSNames []string
|
||||||
|
EmailAddresses []string
|
||||||
|
IPAddresses []net.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// Certificate is an alias on the x509.Certificate to add some methods.
|
||||||
|
Certificate x509.Certificate
|
||||||
|
// CertificateRequest is an alias on the x509.CertificateRequest to add some methods.
|
||||||
|
CertificateRequest x509.CertificateRequest
|
||||||
|
|
||||||
|
// CertificateOptions is used to provide the necessary information to create
|
||||||
|
// a certificate from a certificate sign request.
|
||||||
|
CertificateOptions struct {
|
||||||
|
SerialNumber *big.Int
|
||||||
|
NotBefore time.Time
|
||||||
|
NotAfter time.Time // Validity bounds.
|
||||||
|
IsCA bool
|
||||||
|
// how many sub ca are allowed between this ca and the end/final certificate
|
||||||
|
// if it is -1, then no limit will be set
|
||||||
|
CALength int
|
||||||
|
KeyUsage x509.KeyUsage // for what can the certificate be used
|
||||||
|
KeyExtendedUsage []x509.ExtKeyUsage // extended usage for the certificate
|
||||||
|
CRLUrls []string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a new set of certificate data.
|
||||||
|
func NewCertificateData() *CertificateData {
|
||||||
|
return &CertificateData{Subject: pkix.Name{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a certificate sign request from the input data and the private key of
|
||||||
|
// the request creator.
|
||||||
|
func (c *CertificateData) ToCertificateRequest(private_key PrivateKey) (*CertificateRequest, error) {
|
||||||
|
csr := &x509.CertificateRequest{}
|
||||||
|
|
||||||
|
csr.Subject = c.Subject
|
||||||
|
csr.DNSNames = c.DNSNames
|
||||||
|
csr.IPAddresses = c.IPAddresses
|
||||||
|
csr.EmailAddresses = c.EmailAddresses
|
||||||
|
|
||||||
|
csr_asn1, err := x509.CreateCertificateRequest(rand.Reader, csr, private_key.PrivateKey())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return LoadCertificateSignRequest(csr_asn1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load a certificate sign request from its asn1 representation.
|
||||||
|
func LoadCertificateSignRequest(raw []byte) (*CertificateRequest, error) {
|
||||||
|
csr, err := x509.ParseCertificateRequest(raw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return (*CertificateRequest)(csr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToPem returns a pem.Block representing the CertificateRequest.
|
||||||
|
func (c *CertificateRequest) ToPem() (pem.Block, error) {
|
||||||
|
return pem.Block{Type: PemLabelCertificateRequest, Bytes: c.Raw}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the certificate sign request as a pem block.
|
||||||
|
func (c *CertificateRequest) MarshalPem() (io.WriterTo, error) {
|
||||||
|
if block, err := c.ToPem(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return marshalledPemBlock(pem.EncodeToMemory(&block)), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the certificate sign request to a certificate using the private key
|
||||||
|
// of the signer and the certificate of the signer.
|
||||||
|
// If the certificate is null, the sign request will be used to sign itself.
|
||||||
|
// Please also see the certificate options struct for information on mandatory fields.
|
||||||
|
// For more information, please read http://golang.org/pkg/crypto/x509/#CreateCertificate
|
||||||
|
func (c *CertificateRequest) ToCertificate(private_key PrivateKey,
|
||||||
|
cert_opts CertificateOptions, ca *Certificate) (*Certificate, error) {
|
||||||
|
|
||||||
|
if err := cert_opts.Valid(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
template := &x509.Certificate{}
|
||||||
|
template.Subject = c.Subject
|
||||||
|
template.DNSNames = c.DNSNames
|
||||||
|
template.IPAddresses = c.IPAddresses
|
||||||
|
template.EmailAddresses = c.EmailAddresses
|
||||||
|
|
||||||
|
// if no ca is given, we have to set IsCA to self sign
|
||||||
|
if ca == nil {
|
||||||
|
template.IsCA = true
|
||||||
|
}
|
||||||
|
|
||||||
|
template.NotBefore = cert_opts.NotBefore
|
||||||
|
template.NotAfter = cert_opts.NotAfter
|
||||||
|
template.KeyUsage = cert_opts.KeyUsage
|
||||||
|
template.ExtKeyUsage = cert_opts.KeyExtendedUsage
|
||||||
|
template.CRLDistributionPoints = cert_opts.CRLUrls
|
||||||
|
template.IsCA = cert_opts.IsCA
|
||||||
|
if cert_opts.IsCA {
|
||||||
|
template.BasicConstraintsValid = true
|
||||||
|
}
|
||||||
|
if cert_opts.CALength >= 0 {
|
||||||
|
template.MaxPathLen = cert_opts.CALength
|
||||||
|
template.MaxPathLenZero = true
|
||||||
|
template.BasicConstraintsValid = true
|
||||||
|
}
|
||||||
|
template.SerialNumber = cert_opts.SerialNumber
|
||||||
|
|
||||||
|
var cert_asn1 []byte
|
||||||
|
var err error
|
||||||
|
// if we have no ca which can sign the cert, a self signed cert is wanted
|
||||||
|
// (or isn't it? Maybe we should split creation of the template? But that would be ugly)
|
||||||
|
if ca == nil {
|
||||||
|
cert_asn1, err = x509.CreateCertificate(rand.Reader, template, template, c.PublicKey, private_key.PrivateKey())
|
||||||
|
} else {
|
||||||
|
cert_asn1, err = x509.CreateCertificate(rand.Reader, template, (*x509.Certificate)(ca), c.PublicKey, private_key.PrivateKey())
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return LoadCertificate(cert_asn1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load a certificate from its asn1 representation.
|
||||||
|
func LoadCertificate(raw []byte) (*Certificate, error) {
|
||||||
|
cert, err := x509.ParseCertificate(raw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return (*Certificate)(cert), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// marshal the certificate to a pem block
|
||||||
|
func (c *Certificate) MarshalPem() (io.WriterTo, error) {
|
||||||
|
if block, err := c.ToPem(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return marshalledPemBlock(pem.EncodeToMemory(&block)), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToPem returns the pem block of the certificate.
|
||||||
|
func (c *Certificate) ToPem() (pem.Block, error) {
|
||||||
|
return pem.Block{Type: PemLabelCertificate, Bytes: c.Raw}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the certificate options have the required fields set.
|
||||||
|
func (co *CertificateOptions) Valid() error {
|
||||||
|
if co.SerialNumber == nil {
|
||||||
|
return fmt.Errorf("No serial number set!")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
package pki
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/asn1"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This label is used as the type in the pem encoding of ECDSA private keys.
|
||||||
|
const PemLabelEcdsa = "EC PRIVATE KEY"
|
||||||
|
|
||||||
|
type (
|
||||||
|
// This type handles the function calls to the ecdsa private key by
|
||||||
|
// implementing the interface.
|
||||||
|
EcdsaPrivateKey struct {
|
||||||
|
private_key *ecdsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// EcdsaPublicKey is the specific public key type for ecdsa. It implements the
|
||||||
|
// the PublicKey interface.
|
||||||
|
EcdsaPublicKey struct {
|
||||||
|
public_key *ecdsa.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// This struct is used to marshal and parse the ecdsa signature.
|
||||||
|
signatureEcdsa struct {
|
||||||
|
R, S *big.Int
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a new ECDSA private key using the specified curve.
|
||||||
|
// For available curves, please take a look at the crypto/elliptic package.
|
||||||
|
func NewPrivateKeyEcdsa(curve elliptic.Curve) (*EcdsaPrivateKey, error) {
|
||||||
|
key, err := ecdsa.GenerateKey(curve, rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &EcdsaPrivateKey{key}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the private key from the asn1 representation.
|
||||||
|
func LoadPrivateKeyEcdsa(raw []byte) (*EcdsaPrivateKey, error) {
|
||||||
|
key, err := x509.ParseECPrivateKey(raw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &EcdsaPrivateKey{key}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new public key from the private key.
|
||||||
|
func (pr EcdsaPrivateKey) Public() PublicKey {
|
||||||
|
return &EcdsaPublicKey{pr.private_key.Public().(*ecdsa.PublicKey)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign a message using the private key and the provided hash function.
|
||||||
|
func (pr EcdsaPrivateKey) Sign(message []byte, hash crypto.Hash) ([]byte, error) {
|
||||||
|
empty := make([]byte, 0)
|
||||||
|
if !hash.Available() {
|
||||||
|
return empty, errors.New("Hash method is not available!")
|
||||||
|
}
|
||||||
|
hashed_message := hash.New()
|
||||||
|
hashed_message.Write(message)
|
||||||
|
return pr.private_key.Sign(rand.Reader, hashed_message.Sum(nil), hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function returns the crypto.PrivateKey structure of the ECDSA key.
|
||||||
|
func (pr EcdsaPrivateKey) PrivateKey() crypto.PrivateKey {
|
||||||
|
return pr.private_key
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function implements the Pemmer interface to marshal the private key
|
||||||
|
// into a pem block.
|
||||||
|
func (pr EcdsaPrivateKey) MarshalPem() (io.WriterTo, error) {
|
||||||
|
pem_block, err := pr.ToPem()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return marshalledPemBlock(pem.EncodeToMemory(&pem_block)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function implements ToPem to return the raw pem block.
|
||||||
|
func (pr EcdsaPrivateKey) ToPem() (pem.Block, error) {
|
||||||
|
asn1, err := x509.MarshalECPrivateKey(pr.private_key)
|
||||||
|
if err != nil {
|
||||||
|
return pem.Block{}, err
|
||||||
|
}
|
||||||
|
return pem.Block{Type: PemLabelEcdsa, Bytes: asn1}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This functoin loads an ecdsa public key from the asn.1 representation.
|
||||||
|
func LoadPublicKeyEcdsa(raw []byte) (*EcdsaPublicKey, error) {
|
||||||
|
raw_pub, err := x509.ParsePKIXPublicKey(raw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pub, ok := raw_pub.(*ecdsa.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("Not an ecdsa key!")
|
||||||
|
}
|
||||||
|
return &EcdsaPublicKey{pub}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToPem returns the pem block of the public key.
|
||||||
|
func (pu *EcdsaPublicKey) ToPem() (pem.Block, error) {
|
||||||
|
asn1, err := x509.MarshalPKIXPublicKey(pu.public_key)
|
||||||
|
if err != nil {
|
||||||
|
return pem.Block{}, err
|
||||||
|
}
|
||||||
|
return pem.Block{Type: PemLabelPublic, Bytes: asn1}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function implements the Pemmer interface to marshal the public key into
|
||||||
|
// a pem block.
|
||||||
|
func (pu *EcdsaPublicKey) MarshalPem() (io.WriterTo, error) {
|
||||||
|
if block, err := pu.ToPem(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return marshalledPemBlock(pem.EncodeToMemory(&block)), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function verifies a message using the public key, signature and hash
|
||||||
|
// function.
|
||||||
|
// The hash function must be the same as was used to create the signature.
|
||||||
|
func (pu *EcdsaPublicKey) Verify(message []byte, signature_raw []byte, hash crypto.Hash) (bool, error) {
|
||||||
|
var sig signatureEcdsa
|
||||||
|
_, err := asn1.Unmarshal(signature_raw, &sig)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
hashed_message := hash.New()
|
||||||
|
hashed_message.Write(message)
|
||||||
|
return ecdsa.Verify(pu.public_key, hashed_message.Sum(nil), sig.R, sig.S), nil
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
package pki
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PemLabelEd25519 = "ED25519 PRIVATE KEY" // TODO find correct label
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Ed25519PrivateKey struct {
|
||||||
|
private_key ed25519.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
Ed25519PublicKey struct {
|
||||||
|
public_key ed25519.PublicKey
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a new private key of type ed25519.
|
||||||
|
func NewPrivateKeyEd25519() (*Ed25519PrivateKey, error) {
|
||||||
|
_, pr_raw, err := ed25519.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Ed25519PrivateKey{pr_raw}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore an ed25519 private key from a raw byte stream.
|
||||||
|
// TODO does this have to be asn1? all other functions expect asn1
|
||||||
|
func LoadPrivateKeyEd25519(raw []byte) (*Ed25519PrivateKey, error) {
|
||||||
|
pr_loaded := make([]byte, ed25519.PrivateKeySize)
|
||||||
|
length := copy(pr_loaded, raw)
|
||||||
|
if length != ed25519.PrivateKeySize {
|
||||||
|
return nil, fmt.Errorf("private key length incorrect - got: %d - expected: %d", length, ed25519.PrivateKeySize)
|
||||||
|
}
|
||||||
|
return &Ed25519PrivateKey{pr_loaded}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO implement the raw API for the private key
|
||||||
|
func (pr *Ed25519PrivateKey) PrivateKey() crypto.PrivateKey {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the public key for this private key.
|
||||||
|
func (pr *Ed25519PrivateKey) Public() PublicKey {
|
||||||
|
buf := bytes.NewBufferString(string(pr.private_key[:])) // create a bytes buffer to read the private key
|
||||||
|
pu_raw, _, err := ed25519.GenerateKey(buf) // use the already built private key again
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &Ed25519PublicKey{pu_raw}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash the message given the hash algorythm and sign the hash using the private key.
|
||||||
|
func (pr *Ed25519PrivateKey) Sign(message []byte, hash crypto.Hash) ([]byte, error) {
|
||||||
|
hashed_message := hash.New()
|
||||||
|
hashed_message.Write(message)
|
||||||
|
result := ed25519.Sign(pr.private_key, hashed_message.Sum(nil))[:]
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export the private key into the Pem format.
|
||||||
|
func (pr Ed25519PrivateKey) MarshalPem() (io.WriterTo, error) {
|
||||||
|
pem_block, err := pr.ToPem()
|
||||||
|
if err != nil { // it does not currently return an error, but maybe that will change
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return marshalledPemBlock(pem.EncodeToMemory(&pem_block)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr Ed25519PrivateKey) ToPem() (pem.Block, error) {
|
||||||
|
return pem.Block{Type: PemLabelEd25519, Bytes: pr.private_key[:]}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the public key from a raw byte stream.
|
||||||
|
// TODO should this be read from ASN.1? All other functions do that.
|
||||||
|
func LoadPublicKeyEd25519(raw []byte) (*Ed25519PublicKey, error) {
|
||||||
|
pu_loaded := make([]byte, ed25519.PublicKeySize)
|
||||||
|
length := copy(pu_loaded, raw)
|
||||||
|
if length != ed25519.PublicKeySize {
|
||||||
|
return nil, errors.New("public key length incorrect")
|
||||||
|
}
|
||||||
|
return &Ed25519PublicKey{pu_loaded}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToPem returns the pem encoded public key.
|
||||||
|
func (pu Ed25519PublicKey) ToPem() (pem.Block, error) {
|
||||||
|
return pem.Block{Type: PemLabelPublic, Bytes: pu.public_key[:]}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export the public key into the pem format.
|
||||||
|
func (pu Ed25519PublicKey) MarshalPem() (io.WriterTo, error) {
|
||||||
|
pem_block, err := pu.ToPem()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return marshalledPemBlock(pem.EncodeToMemory(&pem_block)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash the message with the hash algorythm and check the signature against the result.
|
||||||
|
func (pu Ed25519PublicKey) Verify(message []byte, signature []byte, hash crypto.Hash) (bool, error) {
|
||||||
|
sig := make([]byte, ed25519.SignatureSize)
|
||||||
|
length := copy(sig[:], signature)
|
||||||
|
if length != ed25519.SignatureSize {
|
||||||
|
return false, errors.New("signature does not fit length")
|
||||||
|
}
|
||||||
|
hashed_message := hash.New()
|
||||||
|
hashed_message.Write(message)
|
||||||
|
return ed25519.Verify(pu.public_key, hashed_message.Sum(nil), sig), nil
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package pki
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
marshalledPemBlock []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
// This function writes the marshalled pem block to a writer and returns the
|
||||||
|
// number of written bytes and eventual errors.
|
||||||
|
func (b marshalledPemBlock) WriteTo(stream io.Writer) (int64, error) {
|
||||||
|
numBytes, err := stream.Write(b)
|
||||||
|
return int64(numBytes), err
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
package pki
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PemLabelRsa = "RSA PRIVATE KEY"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
RsaPrivateKey struct {
|
||||||
|
private_key *rsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
RsaPublicKey struct {
|
||||||
|
public_key *rsa.PublicKey
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// generate a new rsa private key
|
||||||
|
func NewPrivateKeyRsa(size int) (*RsaPrivateKey, error) {
|
||||||
|
key, err := rsa.GenerateKey(rand.Reader, size)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &RsaPrivateKey{key}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// load a rsa private key its ASN.1 presentation
|
||||||
|
func LoadPrivateKeyRsa(raw []byte) (*RsaPrivateKey, error) {
|
||||||
|
key, err := x509.ParsePKCS1PrivateKey(raw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &RsaPrivateKey{key}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *RsaPrivateKey) Public() PublicKey {
|
||||||
|
return &RsaPublicKey{pr.private_key.Public().(*rsa.PublicKey)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr RsaPrivateKey) Sign(message []byte, hash crypto.Hash) ([]byte, error) {
|
||||||
|
if !hash.Available() {
|
||||||
|
return make([]byte, 0), errors.New("Hash method is not available!")
|
||||||
|
}
|
||||||
|
hashed_message := hash.New()
|
||||||
|
hashed_message.Write(message)
|
||||||
|
return rsa.SignPKCS1v15(rand.Reader, pr.private_key, hash, hashed_message.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the private key
|
||||||
|
func (pr RsaPrivateKey) PrivateKey() crypto.PrivateKey {
|
||||||
|
return pr.private_key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr RsaPrivateKey) MarshalPem() (io.WriterTo, error) {
|
||||||
|
pem_block, err := pr.ToPem()
|
||||||
|
if err != nil { // it does not currently return an error, but maybe that will change
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return marshalledPemBlock(pem.EncodeToMemory(&pem_block)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr RsaPrivateKey) ToPem() (pem.Block, error) {
|
||||||
|
return pem.Block{
|
||||||
|
Type: PemLabelRsa,
|
||||||
|
Bytes: x509.MarshalPKCS1PrivateKey(pr.private_key),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore a rsa public key
|
||||||
|
func LoadPublicKeyRsa(raw []byte) (*RsaPublicKey, error) {
|
||||||
|
pub := &RsaPublicKey{}
|
||||||
|
if pub_raw, err := x509.ParsePKIXPublicKey(raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
pub.public_key = pub_raw.(*rsa.PublicKey)
|
||||||
|
}
|
||||||
|
return pub, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToPem returns the pem encoded public key.
|
||||||
|
func (pu *RsaPublicKey) ToPem() (pem.Block, error) {
|
||||||
|
asn1, err := x509.MarshalPKIXPublicKey(pu.public_key)
|
||||||
|
if err != nil {
|
||||||
|
return pem.Block{}, err
|
||||||
|
}
|
||||||
|
return pem.Block{Type: PemLabelPublic, Bytes: asn1}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// marshal a rsa public key into pem format
|
||||||
|
func (pu *RsaPublicKey) MarshalPem() (io.WriterTo, error) {
|
||||||
|
pem_block, err := pu.ToPem()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return marshalledPemBlock(pem.EncodeToMemory(&pem_block)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify a message with a signature using the public key
|
||||||
|
func (pu *RsaPublicKey) Verify(message []byte, signature []byte, hash crypto.Hash) (bool, error) {
|
||||||
|
hashed_message := hash.New()
|
||||||
|
hashed_message.Write(message)
|
||||||
|
if err := rsa.VerifyPKCS1v15(pu.public_key, hash, hashed_message.Sum(nil), signature); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Package pki provides an easier way to create crypto related structures
|
||||||
|
// with the intent of making the management of these structures easier for
|
||||||
|
// other programs.
|
||||||
|
// Currently it provides mechanisms to create private keys in ECDSA and RSA,
|
||||||
|
// create public keys, create certificate sign requests and certificates.
|
||||||
|
//
|
||||||
|
// To create a new private key, there are two ways
|
||||||
|
// for an ecdsa key
|
||||||
|
// private_key, err := NewPrivateKeyEcdsa(elliptic.P521())
|
||||||
|
// or for a RSA key
|
||||||
|
// private_key, err := NewPrivateKeyRSA(4096)
|
||||||
|
//
|
||||||
|
// Getting a private key from the private key can be done with
|
||||||
|
// public_key := private_key.Public()
|
||||||
|
package pki
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"encoding/pem"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This label is used as the type in the pem encoding of public keys.
|
||||||
|
const PemLabelPublic = "PUBLIC KEY"
|
||||||
|
|
||||||
|
type (
|
||||||
|
// PrivateKey is a common interface for all crypto implementations to provide
|
||||||
|
// the same functions, like deriving a public key or signing a message.
|
||||||
|
PrivateKey interface {
|
||||||
|
// Derive a new public key from the private key.
|
||||||
|
Public() PublicKey
|
||||||
|
// Sign a message using the public key and the given hash method.
|
||||||
|
// To use a hash method, include the package
|
||||||
|
// import _ "crypto/sha512"
|
||||||
|
Sign(message []byte, hash crypto.Hash) ([]byte, error)
|
||||||
|
|
||||||
|
// Return the original go structure of the private key.
|
||||||
|
PrivateKey() crypto.PrivateKey
|
||||||
|
|
||||||
|
// ToPem must return a pem block of the private key.
|
||||||
|
ToPem() (pem.Block, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKey is used by the different crypto implementations to provide the
|
||||||
|
// same functionality like verifying a message against a signature.
|
||||||
|
PublicKey interface {
|
||||||
|
Pemmer
|
||||||
|
PemOutput
|
||||||
|
// This function can be used to verify a message against a provided signature
|
||||||
|
// using the given hash function.
|
||||||
|
Verify(message []byte, signature []byte, hash crypto.Hash) (bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pemmer is used by all crypto structures which need to be available
|
||||||
|
// in the pem format. The result can then be written to any structure
|
||||||
|
// implementing the io.Writer interface.
|
||||||
|
Pemmer interface {
|
||||||
|
MarshalPem() (io.WriterTo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToPem returns the raw pem block to make it possible to write the result to
|
||||||
|
// any place.
|
||||||
|
PemOutput interface {
|
||||||
|
ToPem() (pem.Block, error)
|
||||||
|
}
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
# git.zero-knowledge.org/gibheer/pki v0.0.0-20220815203810-d34a8ab26ec7
|
||||||
|
## explicit; go 1.19
|
||||||
|
git.zero-knowledge.org/gibheer/pki
|
Loading…
Reference in New Issue