Skip to content

Instantly share code, notes, and snippets.

@Tatskaari
Created January 12, 2023 11:02
Show Gist options
  • Save Tatskaari/5e9ff5c51edcaa7d8a3d63d718349cd3 to your computer and use it in GitHub Desktop.
Save Tatskaari/5e9ff5c51edcaa7d8a3d63d718349cd3 to your computer and use it in GitHub Desktop.
CA based signing
package main
import (
"context"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"log"
"os"
"github.com/sigstore/sigstore/pkg/signature/kms/gcp"
)
// PrintCSR creates a signing request for a key stored in google's KMS. We can't do this through openssl like normal,
// because we don't have access to the private key, and google haven't implemented anything like this in their SDK or
// API.
func PrintCSR(key string) error {
s, err := gcp.LoadSignerVerifier(context.Background(), key)
if err != nil {
return err
}
sig, _, err := s.CryptoSigner(context.Background(), nil)
if err != nil {
return err
}
subj := pkix.Name{
CommonName: "release.please.build",
Country: []string{"GB"},
Province: []string{"London"},
Locality: []string{"London"},
Organization: []string{"Thought Machine"},
OrganizationalUnit: []string{"Engineering"},
}
keyUsage := x509.KeyUsageDigitalSignature
extKeyUsage, err := marshalKeyUsage(keyUsage)
if err != nil {
log.Fatal(err)
}
template := x509.CertificateRequest{
Subject: subj,
DNSNames: []string{"releases.please.build"},
ExtraExtensions: []pkix.Extension{extKeyUsage},
}
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &template, sig)
if err != nil {
log.Fatalf("%v", err)
}
return pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes})
}
// marshalKeyUsage creates an extension to specify the key usage as digital sig. This code was borrowed from here:
// https://stackoverflow.com/questions/69412331/how-to-add-keyusage-to-certificate-signing-request-in-golang
func marshalKeyUsage(ku x509.KeyUsage) (pkix.Extension, error) {
// As specified somewhere deep in the pki docs
keyUsageID := asn1.ObjectIdentifier{2, 5, 29, 15}
ext := pkix.Extension{Id: keyUsageID, Critical: true}
var a [2]byte
a[0] = reverseBitsInAByte(byte(ku))
a[1] = reverseBitsInAByte(byte(ku >> 8))
l := 1
if a[1] != 0 {
l = 2
}
bitString := a[:l]
var err error
ext.Value, err = asn1.Marshal(asn1.BitString{Bytes: bitString, BitLength: asn1BitLength(bitString)})
if err != nil {
return ext, err
}
return ext, nil
}
func reverseBitsInAByte(in byte) byte {
b1 := in>>4 | in<<4
b2 := b1>>2&0x33 | b1<<2&0xcc
b3 := b2>>1&0x55 | b2<<1&0xaa
return b3
}
func asn1BitLength(bitString []byte) int {
bitLen := len(bitString) * 8
for i := range bitString {
b := bitString[len(bitString)-i-1]
for bit := uint(0); bit < 8; bit++ {
if (b>>bit)&1 == 1 {
return bitLen
}
bitLen--
}
}
return 0
}
// This program generates a certificate sign request from a key stored in GCP's KMS
//
// Usage: Pass in a key URI like so as the argument to this program:
// csr gcpkms://projects/tm-please/locations/eur5/keyRings/please-release/cryptoKeys/please-release-2/cryptoKeyVersions/1
func main() {
if err := PrintCSR(os.Args[1]); err != nil {
panic(err)
}
}
// SingAndVerify loads a cert chain .pem and verifies the certificate against an embedded root cert. It then signs a message
// with out kms key and verifies the signature with the cert.
func SingAndVerify() {
f, err := os.Open("please.cer")
if err != nil {
log.Fatalf("%v", err)
}
certs, err := cryptoutils.LoadCertificatesFromPEM(f)
if err != nil {
log.Fatalf("%v", err)
}
roots := x509.NewCertPool()
bs, err := os.ReadFile("root.crt")
if err != nil {
log.Fatalf("%v", err)
}
roots.AppendCertsFromPEM(bs)
opts := x509.VerifyOptions{
Roots: roots,
Intermediates: x509.NewCertPool(),
}
// Walk through the chain backwards adding the certs as needed.
for i := range certs {
if i == 0 {
continue
}
cert := certs[len(certs)-i-1]
if _, err := cert.Verify(opts); err != nil {
panic(err)
}
opts.Intermediates.AddCert(cert)
}
// The fist cert is the one we signed with
cert := certs[0]
_, err = cert.Verify(opts)
if err != nil {
log.Fatalf("%v", err)
}
verifier, err := signature.LoadVerifier(certs[0].PublicKey.(crypto.PublicKey), crypto.SHA256)
if err != nil {
log.Fatalf("%v", err)
}
s, err := gcp.LoadSignerVerifier(context.Background(), "gcpkms://projects/tm-please/locations/eur5/keyRings/please-release/cryptoKeys/please-release/cryptoKeyVersions/1")
if err != nil {
log.Fatalf("err: %v", err)
}
sig, err := s.SignMessage(strings.NewReader("message"))
if err != nil {
log.Fatalf("err: %v", err)
}
err = verifier.VerifySignature(bytes.NewReader(sig), strings.NewReader("message"))
if err != nil {
log.Fatalf("err: %v", err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment