Consolidate key/cert/signature encoding and decoding (#1243)

This commit is contained in:
paul cannon 2019-02-07 12:40:28 -06:00 committed by GitHub
parent 0ffdaeebce
commit ef61c170b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 340 additions and 247 deletions

View File

@ -129,9 +129,9 @@ func saveIdentityTar(path string, key *ecdsa.PrivateKey, id storj.NodeID) error
tw := tar.NewWriter(tarData)
caCertBytes, caCertErr := peertls.ChainBytes(ca.Cert)
caKeyBytes, caKeyErr := pkcrypto.KeyBytes(ca.Key)
caKeyBytes, caKeyErr := pkcrypto.PrivateKeyToPEM(ca.Key)
identCertBytes, identCertErr := peertls.ChainBytes(ident.Leaf, ident.CA)
identKeyBytes, identKeyErr := pkcrypto.KeyBytes(ident.Key)
identKeyBytes, identKeyErr := pkcrypto.PrivateKeyToPEM(ident.Key)
if err := errs.Combine(caCertErr, caKeyErr, identCertErr, identKeyErr); err != nil {
return err
}

View File

@ -159,7 +159,7 @@ func cmdAuthorize(cmd *cobra.Command, args []string) error {
return errs.New("error occurred while signing certificate: %s\n(identity files were still generated and saved, if you try again existing files will be loaded)", err)
}
signedChain, err := identity.ParseCertChain(signedChainBytes)
signedChain, err := pkcrypto.CertsFromDER(signedChainBytes)
if err != nil {
return nil
}

View File

@ -7,7 +7,6 @@ import (
"bytes"
"context"
"encoding/base64"
"encoding/pem"
"fmt"
"os"
"os/exec"
@ -101,14 +100,14 @@ func inmemoryTest(flags *Flags, command string, args []string) error {
}
var chainPEM bytes.Buffer
errLeaf := pem.Encode(&chainPEM, pkcrypto.NewCertBlock(identity.Leaf.Raw))
errCA := pem.Encode(&chainPEM, pkcrypto.NewCertBlock(identity.CA.Raw))
errLeaf := pkcrypto.WriteCertPEM(&chainPEM, identity.Leaf)
errCA := pkcrypto.WriteCertPEM(&chainPEM, identity.CA)
if errLeaf != nil || errCA != nil {
return errs.Combine(errLeaf, errCA, planet.Shutdown())
}
var key bytes.Buffer
errKey := pkcrypto.WriteKey(&key, identity.Key)
errKey := pkcrypto.WritePrivateKeyPEM(&key, identity.Key)
if errKey != nil {
return errs.Combine(errKey, planet.Shutdown())
}

View File

@ -9,7 +9,6 @@ package main
import (
"bytes"
"context"
"encoding/pem"
"flag"
"fmt"
"go/format"
@ -58,7 +57,7 @@ func main() {
}
var keys bytes.Buffer
err = peertls.WriteKey(&keys, identity.Key)
err = peertls.WriteKeyPEM(&keys, identity.Key)
if err != nil {
panic(err)
}
@ -86,13 +85,3 @@ func main() {
panic(err)
}
}
func encodeBlocks(blocks ...*pem.Block) ([]byte, error) {
var buf bytes.Buffer
for _, block := range blocks {
if err := pem.Encode(&buf, block); err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}

View File

@ -5,7 +5,6 @@ package auth
import (
"crypto/ecdsa"
"crypto/x509"
"github.com/gogo/protobuf/proto"
"github.com/gtank/cryptopasta"
@ -13,6 +12,7 @@ import (
"storj.io/storj/pkg/identity"
"storj.io/storj/pkg/peertls"
"storj.io/storj/pkg/pkcrypto"
"storj.io/storj/pkg/storj"
)
@ -128,7 +128,7 @@ func VerifyMsg(msg SignableMessage, signer storj.NodeID) error {
}
func parseECDSA(rawCert []byte) (*ecdsa.PublicKey, error) {
cert, err := x509.ParseCertificate(rawCert)
cert, err := pkcrypto.CertFromDER(rawCert)
if err != nil {
return nil, ErrVerify.Wrap(err)
}

View File

@ -293,7 +293,8 @@ func testDatabase(ctx context.Context, t *testing.T, bwdb bwagreement.DB) {
assert.NoError(t, err)
rba.Signature = []byte("invalid")
reply, err := satellite.BandwidthAgreements(ctxSN1, rba)
assert.True(t, auth.ErrSigLen.Has(err) && pb.ErrRenter.Has(err), err.Error())
assert.Error(t, err)
assert.True(t, pb.ErrRenter.Has(err), err.Error())
assert.Equal(t, pb.AgreementsSummary_REJECTED, reply.Status)
}

View File

@ -26,6 +26,7 @@ import (
"storj.io/storj/internal/testplanet"
"storj.io/storj/pkg/identity"
"storj.io/storj/pkg/pb"
"storj.io/storj/pkg/pkcrypto"
"storj.io/storj/pkg/server"
"storj.io/storj/pkg/transport"
"storj.io/storj/pkg/utils"
@ -663,7 +664,7 @@ func TestCertificateSigner_Sign_E2E(t *testing.T) {
require.NoError(t, err)
require.NotEmpty(t, signedChainBytes)
signedChain, err := identity.ParseCertChain(signedChainBytes)
signedChain, err := pkcrypto.CertsFromDER(signedChainBytes)
require.NoError(t, err)
assert.Equal(t, clientIdent.CA.RawTBSCertificate, signedChain[0].RawTBSCertificate)
@ -829,7 +830,7 @@ func TestCertificateSigner_Sign(t *testing.T) {
require.NotNil(t, res)
require.NotEmpty(t, res.Chain)
signedChain, err := identity.ParseCertChain(res.Chain)
signedChain, err := pkcrypto.CertsFromDER(res.Chain)
require.NoError(t, err)
assert.Equal(t, clientIdent.CA.RawTBSCertificate, signedChain[0].RawTBSCertificate)

View File

@ -10,7 +10,6 @@ import (
"crypto/ecdsa"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"io/ioutil"
@ -238,10 +237,9 @@ func (fc FullCAConfig) Load() (*FullCertificateAuthority, error) {
if err != nil {
return nil, peertls.ErrNotExist.Wrap(err)
}
kp, _ := pem.Decode(kb)
k, err := x509.ParseECPrivateKey(kp.Bytes)
k, err := pkcrypto.PrivateKeyFromPEM(kb)
if err != nil {
return nil, errs.New("unable to parse EC private key: %v", err)
return nil, err
}
return &FullCertificateAuthority{
@ -271,7 +269,7 @@ func (fc FullCAConfig) Save(ca *FullCertificateAuthority) error {
}
if fc.KeyPath != "" {
if err := pkcrypto.WriteKey(&keyData, ca.Key); err != nil {
if err := pkcrypto.WritePrivateKeyPEM(&keyData, ca.Key); err != nil {
writeErrs.Add(err)
return writeErrs.Err()
}
@ -299,7 +297,7 @@ func (pc PeerCAConfig) Load() (*PeerCertificateAuthority, error) {
return nil, peertls.ErrNotExist.Wrap(err)
}
chain, err := DecodeAndParseChainPEM(chainPEM)
chain, err := pkcrypto.CertsFromPEM(chainPEM)
if err != nil {
return nil, errs.New("failed to load identity %#v: %v",
pc.CertPath, err)
@ -408,7 +406,7 @@ func (ca *FullCertificateAuthority) Sign(cert *x509.Certificate) (*x509.Certific
return nil, errs.Wrap(err)
}
signedCert, err := x509.ParseCertificate(signedCertBytes)
signedCert, err := pkcrypto.CertFromDER(signedCertBytes)
if err != nil {
return nil, errs.Wrap(err)
}

View File

@ -85,15 +85,11 @@ func FullIdentityFromPEM(chainPEM, keyPEM []byte) (*FullIdentity, error) {
return nil, err
}
keysBytes, err := decodePEM(keyPEM)
if err != nil {
return nil, errs.Wrap(err)
}
// NB: there shouldn't be multiple keys in the key file but if there
// are, this uses the first one
key, err := x509.ParseECPrivateKey(keysBytes[0])
key, err := pkcrypto.PrivateKeyFromPEM(keyPEM)
if err != nil {
return nil, errs.New("unable to parse EC private key: %v", err)
return nil, err
}
return &FullIdentity{
@ -108,12 +104,12 @@ func FullIdentityFromPEM(chainPEM, keyPEM []byte) (*FullIdentity, error) {
// PeerIdentityFromPEM loads a PeerIdentity from a certificate chain and
// private key PEM-encoded bytes
func PeerIdentityFromPEM(chainPEM []byte) (*PeerIdentity, error) {
chain, err := DecodeAndParseChainPEM(chainPEM)
chain, err := pkcrypto.CertsFromPEM(chainPEM)
if err != nil {
return nil, errs.Wrap(err)
}
if len(chain) < peertls.CAIndex+1 {
return nil, ErrChainLength.New("identity chain does not contain a CA certificate")
return nil, pkcrypto.ErrChainLength.New("identity chain does not contain a CA certificate")
}
nodeID, err := NodeIDFromKey(chain[peertls.CAIndex].PublicKey)
if err != nil {
@ -128,19 +124,6 @@ func PeerIdentityFromPEM(chainPEM []byte) (*PeerIdentity, error) {
}, nil
}
// ParseCertChain converts a chain of certificate bytes into x509 certs
func ParseCertChain(chain [][]byte) ([]*x509.Certificate, error) {
c := make([]*x509.Certificate, len(chain))
for i, ct := range chain {
cp, err := x509.ParseCertificate(ct)
if err != nil {
return nil, errs.Wrap(err)
}
c[i] = cp
}
return c, nil
}
// PeerIdentityFromCerts loads a PeerIdentity from a pair of leaf and ca x509 certificates
func PeerIdentityFromCerts(leaf, ca *x509.Certificate, rest []*x509.Certificate) (*PeerIdentity, error) {
i, err := NodeIDFromKey(ca.PublicKey)
@ -192,7 +175,7 @@ func NodeIDFromCertPath(certPath string) (storj.NodeID, error) {
// NodeIDFromPEM loads a node ID from certificate bytes
func NodeIDFromPEM(pemBytes []byte) (storj.NodeID, error) {
chain, err := DecodeAndParseChainPEM(pemBytes)
chain, err := pkcrypto.CertsFromPEM(pemBytes)
if err != nil {
return storj.NodeID{}, Error.New("invalid identity certificate")
}
@ -299,7 +282,7 @@ func (ic Config) Save(fi *FullIdentity) error {
}
if ic.KeyPath != "" {
writeKeyErr = pkcrypto.WriteKey(&keyData, fi.Key)
writeKeyErr = pkcrypto.WritePrivateKeyPEM(&keyData, fi.Key)
writeKeyDataErr = writeKeyData(ic.KeyPath, keyData.Bytes())
}

View File

@ -7,8 +7,6 @@ import (
"bytes"
"context"
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
"os"
"runtime"
"testing"
@ -70,15 +68,11 @@ func TestFullIdentityFromPEM(t *testing.T) {
assert.NotEmpty(t, leafCert)
chainPEM := bytes.NewBuffer([]byte{})
assert.NoError(t, pem.Encode(chainPEM, pkcrypto.NewCertBlock(leafCert.Raw)))
assert.NoError(t, pem.Encode(chainPEM, pkcrypto.NewCertBlock(caCert.Raw)))
leafKeyBytes, err := x509.MarshalECPrivateKey(leafKey)
assert.NoError(t, err)
assert.NotEmpty(t, leafKeyBytes)
assert.NoError(t, pkcrypto.WriteCertPEM(chainPEM, leafCert))
assert.NoError(t, pkcrypto.WriteCertPEM(chainPEM, caCert))
keyPEM := bytes.NewBuffer([]byte{})
assert.NoError(t, pem.Encode(keyPEM, pkcrypto.NewKeyBlock(leafKeyBytes)))
assert.NoError(t, pkcrypto.WritePrivateKeyPEM(keyPEM, leafKey))
fullIdent, err := identity.FullIdentityFromPEM(chainPEM.Bytes(), keyPEM.Bytes())
assert.NoError(t, err)
@ -98,22 +92,18 @@ func TestConfig_SaveIdentity(t *testing.T) {
fi := pregeneratedIdentity(t)
chainPEM := bytes.NewBuffer([]byte{})
assert.NoError(t, pem.Encode(chainPEM, pkcrypto.NewCertBlock(fi.Leaf.Raw)))
assert.NoError(t, pem.Encode(chainPEM, pkcrypto.NewCertBlock(fi.CA.Raw)))
assert.NoError(t, pkcrypto.WriteCertPEM(chainPEM, fi.Leaf))
assert.NoError(t, pkcrypto.WriteCertPEM(chainPEM, fi.CA))
privateKey, ok := fi.Key.(*ecdsa.PrivateKey)
assert.True(t, ok)
assert.NotEmpty(t, privateKey)
keyBytes, err := x509.MarshalECPrivateKey(privateKey)
assert.NoError(t, err)
assert.NotEmpty(t, keyBytes)
keyPEM := bytes.NewBuffer([]byte{})
assert.NoError(t, pem.Encode(keyPEM, pkcrypto.NewKeyBlock(keyBytes)))
assert.NoError(t, pkcrypto.WritePrivateKeyPEM(keyPEM, privateKey))
{ // test saving
err = ic.Save(fi)
err := ic.Save(fi)
assert.NoError(t, err)
certInfo, err := os.Stat(ic.CertPath)

View File

@ -4,18 +4,11 @@
package identity
import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"io/ioutil"
"os"
"path/filepath"
"github.com/zeebo/errs"
"storj.io/storj/pkg/pkcrypto"
"storj.io/storj/pkg/utils"
)
// TLSFilesStatus is the status of keys
@ -30,66 +23,10 @@ const (
)
var (
// ErrChainLength is used when the length of a cert chain isn't what was expected
ErrChainLength = errs.Class("cert chain length error")
// ErrZeroBytes is returned for zero slice
ErrZeroBytes = errs.New("byte slice was unexpectedly empty")
)
type encodedChain struct {
chain [][]byte
extensions [][][]byte
}
// DecodeAndParseChainPEM parses a PEM chain
func DecodeAndParseChainPEM(PEMBytes []byte) ([]*x509.Certificate, error) {
var (
encChain encodedChain
blockErrs utils.ErrorGroup
)
for {
var pemBlock *pem.Block
pemBlock, PEMBytes = pem.Decode(PEMBytes)
if pemBlock == nil {
break
}
switch pemBlock.Type {
case pkcrypto.BlockTypeCertificate:
encChain.AddCert(pemBlock.Bytes)
case pkcrypto.BlockTypeExtension:
if err := encChain.AddExtension(pemBlock.Bytes); err != nil {
blockErrs.Add(err)
}
}
}
if err := blockErrs.Finish(); err != nil {
return nil, err
}
return encChain.Parse()
}
func decodePEM(PEMBytes []byte) ([][]byte, error) {
var DERBytes [][]byte
for {
var DERBlock *pem.Block
DERBlock, PEMBytes = pem.Decode(PEMBytes)
if DERBlock == nil {
break
}
DERBytes = append(DERBytes, DERBlock.Bytes)
}
if len(DERBytes) == 0 || len(DERBytes[0]) == 0 {
return nil, ErrZeroBytes
}
return DERBytes, nil
}
// writeChainData writes data to path ensuring permissions are appropriate for a cert
func writeChainData(path string, data []byte) error {
err := writeFile(path, 0744, 0644, data)
@ -150,43 +87,3 @@ func (t TLSFilesStatus) String() string {
}
return ""
}
func (e *encodedChain) AddCert(b []byte) {
e.chain = append(e.chain, b)
e.extensions = append(e.extensions, [][]byte{})
}
func (e *encodedChain) AddExtension(b []byte) error {
chainLen := len(e.chain)
if chainLen < 1 {
return ErrChainLength.New("expected: >= 1; actual: %d", chainLen)
}
i := chainLen - 1
e.extensions[i] = append(e.extensions[i], b)
return nil
}
func (e *encodedChain) Parse() ([]*x509.Certificate, error) {
chain, err := ParseCertChain(e.chain)
if err != nil {
return nil, err
}
var extErrs utils.ErrorGroup
for i, cert := range chain {
for _, ee := range e.extensions[i] {
ext := pkix.Extension{}
_, err := asn1.Unmarshal(ee, &ext)
if err != nil {
extErrs.Add(err)
}
cert.ExtraExtensions = append(cert.ExtraExtensions, ext)
}
}
if err := extErrs.Finish(); err != nil {
return nil, err
}
return chain, nil
}

View File

@ -10,8 +10,6 @@ import (
"crypto/rand"
"crypto/tls"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"io"
"github.com/zeebo/errs"
@ -23,10 +21,10 @@ import (
var (
// ErrNotExist is used when a file or directory doesn't exist.
ErrNotExist = errs.Class("file or directory not found error")
// ErrTLSTemplate is used when an error occurs during tls template generation.
ErrTLSTemplate = errs.Class("tls template error")
// ErrGenerate is used when an error occurred during cert/key generation.
ErrGenerate = errs.Class("tls generation error")
// ErrTLSTemplate is used when an error occurs during tls template generation.
ErrTLSTemplate = errs.Class("tls template error")
// ErrVerifyPeerCert is used when an error occurs during `VerifyPeerCertificate`.
ErrVerifyPeerCert = errs.Class("tls peer certificate verification error")
// ErrVerifyCertificateChain is used when a certificate chain can't be verified from leaf to root
@ -44,7 +42,7 @@ type PeerCertVerificationFunc func([][]byte, [][]*x509.Certificate) error
// functions and adds certificate parsing.
func VerifyPeerFunc(next ...PeerCertVerificationFunc) PeerCertVerificationFunc {
return func(chain [][]byte, _ [][]*x509.Certificate) error {
c, err := pkcrypto.ParseCertificates(chain)
c, err := pkcrypto.CertsFromDER(chain)
if err != nil {
return ErrVerifyPeerCert.Wrap(err)
}
@ -87,7 +85,7 @@ func VerifyCAWhitelist(cas []*x509.Certificate) PeerCertVerificationFunc {
func TLSCert(chain [][]byte, leaf *x509.Certificate, key crypto.PrivateKey) (*tls.Certificate, error) {
var err error
if leaf == nil {
leaf, err = x509.ParseCertificate(chain[0])
leaf, err = pkcrypto.CertFromDER(chain[0])
if err != nil {
return nil, err
}
@ -108,16 +106,12 @@ func WriteChain(w io.Writer, chain ...*x509.Certificate) error {
var extErrs utils.ErrorGroup
for _, c := range chain {
if err := pem.Encode(w, pkcrypto.NewCertBlock(c.Raw)); err != nil {
if err := pkcrypto.WriteCertPEM(w, c); err != nil {
return errs.Wrap(err)
}
for _, e := range c.ExtraExtensions {
extBytes, err := asn1.Marshal(e)
if err != nil {
extErrs.Add(errs.Wrap(err))
}
if err := pem.Encode(w, pkcrypto.NewExtensionBlock(extBytes)); err != nil {
if err := pkcrypto.WritePKIXExtensionPEM(w, &e); err != nil {
extErrs.Add(errs.Wrap(err))
}
}
@ -162,7 +156,7 @@ func NewCert(key, parentKey crypto.PrivateKey, template, parent *x509.Certificat
return nil, errs.Wrap(err)
}
cert, err := x509.ParseCertificate(cb)
cert, err := pkcrypto.CertFromDER(cb)
if err != nil {
return nil, errs.Wrap(err)
}

View File

@ -8,12 +8,20 @@ import (
)
const (
// BlockTypeEcPrivateKey is the value to define a block type of private key
BlockTypeEcPrivateKey = "EC PRIVATE KEY"
// BlockTypeCertificate is the value to define a block type of certificates
BlockTypeCertificate = "CERTIFICATE"
// BlockTypeExtension is the value to define a block type of certificate extensions
BlockTypeExtension = "EXTENSION"
// BlockLabelEcPrivateKey is the value to define a block label of EC private key
// (which is used here only for backwards compatibility). Use a general PKCS#8
// encoding instead.
BlockLabelEcPrivateKey = "EC PRIVATE KEY"
// BlockLabelPrivateKey is the value to define a block label of general private key
// (used for PKCS#8-encoded private keys of type RSA, ECDSA, and others).
BlockLabelPrivateKey = "PRIVATE KEY"
// BlockLabelPublicKey is the value to define a block label of general public key
// (used for PKIX-encoded public keys of type RSA, ECDSA, and others).
BlockLabelPublicKey = "PUBLIC KEY"
// BlockLabelCertificate is the value to define a block label of certificates
BlockLabelCertificate = "CERTIFICATE"
// BlockLabelExtension is the value to define a block label of certificate extensions
BlockLabelExtension = "EXTENSION"
)
var (
@ -25,4 +33,6 @@ var (
ErrSign = errs.Class("unable to generate signature")
// ErrVerifySignature is used when a cert-chain signature verificaion error occurs.
ErrVerifySignature = errs.Class("tls certificate signature verification error")
// ErrChainLength is used when the length of a cert chain isn't what was expected
ErrChainLength = errs.Class("cert chain length error")
)

View File

@ -4,75 +4,313 @@
package pkcrypto
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"io"
"math/big"
"github.com/zeebo/errs"
"storj.io/storj/pkg/utils"
)
// WriteKey writes the private key, PEM-encoded.
func WriteKey(w io.Writer, key crypto.PrivateKey) error {
var (
kb []byte
err error
)
switch k := key.(type) {
case *ecdsa.PrivateKey:
kb, err = x509.MarshalECPrivateKey(k)
if err != nil {
return errs.Wrap(err)
}
default:
return ErrUnsupportedKey.New("%T", k)
// WritePublicKeyPEM writes the public key, in a PEM-enveloped
// PKIX form.
func WritePublicKeyPEM(w io.Writer, key crypto.PublicKey) error {
kb, err := PublicKeyToPKIX(key)
if err != nil {
return err
}
err = pem.Encode(w, &pem.Block{Type: BlockLabelPublicKey, Bytes: kb})
return errs.Wrap(err)
}
if err := pem.Encode(w, NewKeyBlock(kb)); err != nil {
// PublicKeyToPEM encodes a public key to a PEM-enveloped PKIX form.
func PublicKeyToPEM(key crypto.PublicKey) ([]byte, error) {
kb, err := PublicKeyToPKIX(key)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{Type: BlockLabelPublicKey, Bytes: kb}), nil
}
// PublicKeyToPKIX serializes a public key to a PKIX-encoded form.
func PublicKeyToPKIX(key crypto.PublicKey) ([]byte, error) {
return x509.MarshalPKIXPublicKey(key)
}
// PublicKeyFromPKIX parses a public key from its PKIX encoding.
func PublicKeyFromPKIX(pkixData []byte) (crypto.PublicKey, error) {
return x509.ParsePKIXPublicKey(pkixData)
}
// PublicKeyFromPEM parses a public key from its PEM-enveloped PKIX
// encoding.
func PublicKeyFromPEM(pemData []byte) (crypto.PublicKey, error) {
pb, _ := pem.Decode(pemData)
if pb == nil {
return nil, ErrParseCerts.New("could not parse PEM encoding")
}
if pb.Type != BlockLabelPublicKey {
return nil, ErrParseCerts.New("can not parse public key from PEM block labeled %q", pb.Type)
}
return PublicKeyFromPKIX(pb.Bytes)
}
// WritePrivateKeyPEM writes the private key to the writer, in a PEM-enveloped
// PKCS#8 form.
func WritePrivateKeyPEM(w io.Writer, key crypto.PrivateKey) error {
kb, err := PrivateKeyToPKCS8(key)
if err != nil {
return errs.Wrap(err)
}
return nil
err = pem.Encode(w, &pem.Block{Type: BlockLabelPrivateKey, Bytes: kb})
return errs.Wrap(err)
}
// KeyBytes returns bytes of the private key, PEM-encoded.
func KeyBytes(key crypto.PrivateKey) ([]byte, error) {
var data bytes.Buffer
err := WriteKey(&data, key)
return data.Bytes(), err
// PrivateKeyToPEM serializes a private key to a PEM-enveloped PKCS#8 form.
func PrivateKeyToPEM(key crypto.PrivateKey) ([]byte, error) {
kb, err := PrivateKeyToPKCS8(key)
if err != nil {
return nil, errs.Wrap(err)
}
return pem.EncodeToMemory(&pem.Block{Type: BlockLabelPrivateKey, Bytes: kb}), nil
}
// NewKeyBlock converts an ASN1/DER-encoded byte-slice of a private key into
// a `pem.Block` pointer.
func NewKeyBlock(b []byte) *pem.Block {
return &pem.Block{Type: BlockTypeEcPrivateKey, Bytes: b}
// PrivateKeyToPKCS8 serializes a private key to a PKCS#8-encoded form.
func PrivateKeyToPKCS8(key crypto.PrivateKey) ([]byte, error) {
return x509.MarshalPKCS8PrivateKey(key)
}
// NewCertBlock converts an ASN1/DER-encoded byte-slice of a tls certificate
// into a `pem.Block` pointer.
func NewCertBlock(b []byte) *pem.Block {
return &pem.Block{Type: BlockTypeCertificate, Bytes: b}
// PrivateKeyFromPKCS8 parses a private key from its PKCS#8 encoding.
func PrivateKeyFromPKCS8(keyBytes []byte) (crypto.PrivateKey, error) {
key, err := x509.ParsePKCS8PrivateKey(keyBytes)
if err != nil {
return nil, err
}
return crypto.PrivateKey(key), nil
}
// NewExtensionBlock converts an ASN1/DER-encoded byte-slice of a tls certificate
// extension into a `pem.Block` pointer.
func NewExtensionBlock(b []byte) *pem.Block {
return &pem.Block{Type: BlockTypeExtension, Bytes: b}
// PrivateKeyFromPEM parses a private key from its PEM-enveloped PKCS#8
// encoding.
func PrivateKeyFromPEM(keyBytes []byte) (crypto.PrivateKey, error) {
pb, _ := pem.Decode(keyBytes)
if pb == nil {
return nil, ErrParseCerts.New("could not parse PEM encoding")
}
switch pb.Type {
case BlockLabelEcPrivateKey:
return ecPrivateKeyFromASN1(pb.Bytes)
case BlockLabelPrivateKey:
return PrivateKeyFromPKCS8(pb.Bytes)
}
return nil, ErrParseCerts.New("can not parse private key from PEM block labeled %q", pb.Type)
}
// ParseCertificates parses an x509 certificate from each of the given byte
// slices, which should be encoded in DER. (Unwrap PEM encoding first if
// necessary.)
func ParseCertificates(rawCerts [][]byte) ([]*x509.Certificate, error) {
// WriteCertPEM writes the certificate to the writer, in a PEM-enveloped DER
// encoding.
func WriteCertPEM(w io.Writer, cert *x509.Certificate) error {
err := pem.Encode(w, &pem.Block{Type: BlockLabelCertificate, Bytes: cert.Raw})
return errs.Wrap(err)
}
// CertToPEM returns the bytes of the certificate, in a PEM-enveloped DER
// encoding.
func CertToPEM(cert *x509.Certificate) []byte {
return pem.EncodeToMemory(&pem.Block{Type: BlockLabelCertificate, Bytes: cert.Raw})
}
// CertToDER returns the bytes of the certificate, in a DER encoding.
//
// Note that this is fairly useless, as x509.Certificate objects are always
// supposed to have a member containing the raw DER encoding. But this is
// included for completeness with the rest of this module's API.
func CertToDER(cert *x509.Certificate) ([]byte, error) {
return cert.Raw, nil
}
// CertFromDER parses an X.509 certificate from its DER encoding.
func CertFromDER(certDER []byte) (*x509.Certificate, error) {
return x509.ParseCertificate(certDER)
}
// CertFromPEM parses an X.509 certificate from its PEM-enveloped DER encoding.
func CertFromPEM(certPEM []byte) (*x509.Certificate, error) {
kb, _ := pem.Decode(certPEM)
if kb == nil {
return nil, ErrParseCerts.New("could not decode certificate as PEM")
}
if kb.Type != BlockLabelCertificate {
return nil, ErrParseCerts.New("can not parse certificate from PEM block labeled %q", kb.Type)
}
return CertFromDER(kb.Bytes)
}
// CertsFromDER parses an x509 certificate from each of the given byte
// slices, which should be encoded in DER.
func CertsFromDER(rawCerts [][]byte) ([]*x509.Certificate, error) {
certs := make([]*x509.Certificate, len(rawCerts))
for i, c := range rawCerts {
var err error
certs[i], err = x509.ParseCertificate(c)
certs[i], err = CertFromDER(c)
if err != nil {
return nil, ErrParseCerts.New("unable to parse certificate at index %d", i)
}
}
return certs, nil
}
// CertsFromPEM parses a PEM chain from a single byte string (the PEM-enveloped
// certificates should be concatenated). The PEM blocks may include PKIX
// extensions.
func CertsFromPEM(pemBytes []byte) ([]*x509.Certificate, error) {
var (
encChain encodedChain
blockErrs utils.ErrorGroup
)
for {
var pemBlock *pem.Block
pemBlock, pemBytes = pem.Decode(pemBytes)
if pemBlock == nil {
break
}
switch pemBlock.Type {
case BlockLabelCertificate:
encChain.AddCert(pemBlock.Bytes)
case BlockLabelExtension:
if err := encChain.AddExtension(pemBlock.Bytes); err != nil {
blockErrs.Add(err)
}
}
}
if err := blockErrs.Finish(); err != nil {
return nil, err
}
return encChain.Parse()
}
type encodedChain struct {
chain [][]byte
extensions [][][]byte
}
func (e *encodedChain) AddCert(b []byte) {
e.chain = append(e.chain, b)
e.extensions = append(e.extensions, [][]byte{})
}
func (e *encodedChain) AddExtension(b []byte) error {
chainLen := len(e.chain)
if chainLen < 1 {
return ErrChainLength.New("expected: >= 1; actual: %d", chainLen)
}
i := chainLen - 1
e.extensions[i] = append(e.extensions[i], b)
return nil
}
func (e *encodedChain) Parse() ([]*x509.Certificate, error) {
chain, err := CertsFromDER(e.chain)
if err != nil {
return nil, err
}
var extErrs utils.ErrorGroup
for i, cert := range chain {
for _, ee := range e.extensions[i] {
ext, err := PKIXExtensionFromASN1(ee)
if err != nil {
extErrs.Add(err)
}
cert.ExtraExtensions = append(cert.ExtraExtensions, *ext)
}
}
if err := extErrs.Finish(); err != nil {
return nil, err
}
return chain, nil
}
// WritePKIXExtensionPEM writes the certificate extension to the writer, in a PEM-
// enveloped PKIX form.
func WritePKIXExtensionPEM(w io.Writer, extension *pkix.Extension) error {
extBytes, err := PKIXExtensionToASN1(extension)
if err != nil {
return errs.Wrap(err)
}
err = pem.Encode(w, &pem.Block{Type: BlockLabelExtension, Bytes: extBytes})
return errs.Wrap(err)
}
// PKIXExtensionToPEM serializes a PKIX certificate extension to PEM-
// enveloped ASN.1 bytes.
func PKIXExtensionToPEM(extension *pkix.Extension) ([]byte, error) {
asn, err := PKIXExtensionToASN1(extension)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{Type: BlockLabelExtension, Bytes: asn}), nil
}
// PKIXExtensionToASN1 serializes a PKIX certificate extension to the
// appropriate ASN.1 structure for such things. See RFC 5280, section 4.1.1.2.
func PKIXExtensionToASN1(extension *pkix.Extension) ([]byte, error) {
extBytes, err := asn1.Marshal(extension)
return extBytes, errs.Wrap(err)
}
// PKIXExtensionFromASN1 deserializes a PKIX certificate extension from
// the appropriate ASN.1 structure for such things.
func PKIXExtensionFromASN1(extData []byte) (*pkix.Extension, error) {
var extension pkix.Extension
if _, err := asn1.Unmarshal(extData, &extension); err != nil {
return nil, ErrParseCerts.New("unable to unmarshal PKIX extension: %v", err)
}
return &extension, nil
}
// PKIXExtensionFromPEM parses a PKIX certificate extension from
// PEM-enveloped ASN.1 bytes.
func PKIXExtensionFromPEM(pemBytes []byte) (*pkix.Extension, error) {
pb, _ := pem.Decode(pemBytes)
if pb == nil {
return nil, ErrParseCerts.New("unable to parse PEM block")
}
if pb.Type != BlockLabelExtension {
return nil, ErrParseCerts.New("can not parse PKIX cert extension from PEM block labeled %q", pb.Type)
}
return PKIXExtensionFromASN1(pb.Bytes)
}
type ecdsaSignature struct {
R, S *big.Int
}
func marshalECDSASignature(r, s *big.Int) ([]byte, error) {
return asn1.Marshal(ecdsaSignature{R: r, S: s})
}
func unmarshalECDSASignature(signatureBytes []byte) (r, s *big.Int, err error) {
var signature ecdsaSignature
if _, err = asn1.Unmarshal(signatureBytes, &signature); err != nil {
return nil, nil, err
}
return signature.R, signature.S, nil
}
// ecPrivateKeyFromASN1 parses a private key from the special Elliptic Curve
// Private Key ASN.1 structure. This is here only for backward compatibility.
// Use PKCS#8 instead.
func ecPrivateKeyFromASN1(privKeyData []byte) (crypto.PrivateKey, error) {
key, err := x509.ParseECPrivateKey(privKeyData)
if err != nil {
return nil, err
}
return crypto.PrivateKey(key), nil
}

View File

@ -8,16 +8,8 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/asn1"
"math/big"
)
// ECDSASignature holds the `r` and `s` values in an ecdsa signature
// (see https://golang.org/pkg/crypto/ecdsa)
type ECDSASignature struct {
R, S *big.Int
}
var authECCurve = elliptic.P256()
// GeneratePrivateKey returns a new PrivateKey for signing messages
@ -32,12 +24,12 @@ func VerifySignature(signedData, data []byte, pubKey crypto.PublicKey) error {
return ErrUnsupportedKey.New("%T", key)
}
signature := new(ECDSASignature)
if _, err := asn1.Unmarshal(signedData, signature); err != nil {
r, s, err := unmarshalECDSASignature(signedData)
if err != nil {
return ErrVerifySignature.New("unable to unmarshal ecdsa signature: %v", err)
}
digest := SHA256Hash(data)
if !ecdsa.Verify(key, digest, signature.R, signature.S) {
if !ecdsa.Verify(key, digest, r, s) {
return ErrVerifySignature.New("signature is not valid")
}
return nil
@ -56,7 +48,7 @@ func SignBytes(key crypto.PrivateKey, data []byte) ([]byte, error) {
return nil, ErrSign.Wrap(err)
}
return asn1.Marshal(ECDSASignature{R: r, S: s})
return marshalECDSASignature(r, s)
}
// SignHashOf signs a SHA-256 digest of the given data and returns the new

View File

@ -10,6 +10,7 @@ import (
"storj.io/storj/pkg/identity"
"storj.io/storj/pkg/peertls"
"storj.io/storj/pkg/pkcrypto"
)
// Options holds config, identity, and peer verification function data for use with a grpc server.
@ -53,7 +54,7 @@ func (opts *Options) configure(c Config) (err error) {
return Error.New("unable to find whitelist file %v: %v", c.PeerCAWhitelistPath, err)
}
}
parsed, err := identity.DecodeAndParseChainPEM(whitelist)
parsed, err := pkcrypto.CertsFromPEM(whitelist)
if err != nil {
return Error.Wrap(err)
}