2022-08-25 14:37:22 -04:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
2023-02-02 17:15:17 -08:00
|
|
|
|
2021-06-22 22:26:26 -04:00
|
|
|
package credentialstores
|
|
|
|
|
|
|
|
|
|
import (
|
2022-08-25 14:37:22 -04:00
|
|
|
"context"
|
2021-06-22 22:26:26 -04:00
|
|
|
"encoding/pem"
|
|
|
|
|
"fmt"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"github.com/hashicorp/boundary/internal/errors"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// extractClientCertAndPk takes the values passed into the api for client
|
|
|
|
|
// certificate and client certificate key and parses them into pem blocks.
|
|
|
|
|
// Any non certificate pem blocks are treated as a private key. An error is
|
|
|
|
|
// returned if there is more than 1 private key provided across both fields.
|
2022-08-25 14:37:22 -04:00
|
|
|
func extractClientCertAndPk(ctx context.Context, cert, pk string) ([]*pem.Block, *pem.Block, error) {
|
2021-06-22 22:26:26 -04:00
|
|
|
const op = "credentialstores.extractClientCertAndPk"
|
2022-08-25 14:37:22 -04:00
|
|
|
pks, err := decodePemBlocks(ctx, pk)
|
2021-06-22 22:26:26 -04:00
|
|
|
if err != nil {
|
2022-08-25 14:37:22 -04:00
|
|
|
return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("failed to parse client certificate private key"))
|
2021-06-22 22:26:26 -04:00
|
|
|
}
|
|
|
|
|
var pkPem *pem.Block
|
|
|
|
|
switch len(pks) {
|
|
|
|
|
case 0:
|
|
|
|
|
case 1:
|
|
|
|
|
pkPem = pks[0]
|
|
|
|
|
default:
|
2022-08-25 14:37:22 -04:00
|
|
|
return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "private key payload contained multiple pem blocks")
|
2021-06-22 22:26:26 -04:00
|
|
|
}
|
|
|
|
|
|
2022-08-25 14:37:22 -04:00
|
|
|
bs, err := decodePemBlocks(ctx, cert)
|
2021-06-22 22:26:26 -04:00
|
|
|
if err != nil {
|
2022-08-25 14:37:22 -04:00
|
|
|
return nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("failed to parse client certificate into pem blocks"))
|
2021-06-22 22:26:26 -04:00
|
|
|
}
|
|
|
|
|
pkIdx := -1
|
|
|
|
|
for i, b := range bs {
|
|
|
|
|
if !strings.Contains(b.Type, "CERTIFICATE") {
|
|
|
|
|
switch {
|
|
|
|
|
case pkPem == nil:
|
|
|
|
|
pkIdx, pkPem = i, b
|
|
|
|
|
case pkIdx < 0:
|
2022-08-25 14:37:22 -04:00
|
|
|
return nil, nil, errors.New(ctx, errors.InvalidParameter, op, "client certificate contains a private key when one was also provided separately")
|
2021-06-22 22:26:26 -04:00
|
|
|
default:
|
2022-08-25 14:37:22 -04:00
|
|
|
return nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("second primary key found at %d after previous one found at %d", i, pkIdx))
|
2021-06-22 22:26:26 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if pkIdx >= 0 {
|
|
|
|
|
bs = append(bs[:pkIdx], bs[pkIdx+1:]...)
|
|
|
|
|
}
|
|
|
|
|
return bs, pkPem, nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-25 14:37:22 -04:00
|
|
|
func decodePemBlocks(ctx context.Context, input string) ([]*pem.Block, error) {
|
2021-06-22 22:26:26 -04:00
|
|
|
const op = "credentialstores.decodePemBlocks"
|
|
|
|
|
cpIn := make([]byte, len(input))
|
|
|
|
|
copy(cpIn, input)
|
|
|
|
|
var p *pem.Block
|
|
|
|
|
var ret []*pem.Block
|
|
|
|
|
for {
|
|
|
|
|
p, cpIn = pem.Decode(cpIn)
|
|
|
|
|
if p == nil {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
ret = append(ret, p)
|
|
|
|
|
}
|
|
|
|
|
if len(cpIn) > 0 {
|
2022-08-25 14:37:22 -04:00
|
|
|
return nil, errors.New(ctx, errors.InvalidParameter, op, "not all data parseable by pem block")
|
2021-06-22 22:26:26 -04:00
|
|
|
}
|
|
|
|
|
return ret, nil
|
|
|
|
|
}
|