17 // Sign signs the given HTTP request with the matching private key of the
18 // public key available at pubkeyURL.
19 func Sign(req *http.Request, key *rsa.PrivateKey, pubkeyURL string) error {
21 return fmt.Errorf("no pubkey url")
23 date := time.Now().UTC().Format(http.TimeFormat)
24 req.Header.Set("Date", date)
26 toSign := []string{"(request-target)", "host", "date"}
27 fmt.Fprintln(hash, "(request-target):", strings.ToLower(req.Method), req.URL.Path)
28 fmt.Fprintln(hash, "host:", req.URL.Hostname())
29 fmt.Fprintf(hash, "date: %s", date)
32 // we're adding one more entry to our signature, so one more line.
33 fmt.Fprint(hash, "\n")
34 buf := &bytes.Buffer{}
35 io.Copy(buf, req.Body)
37 req.Body = io.NopCloser(buf)
38 digest := sha256.Sum256(buf.Bytes())
39 d := "SHA-256=" + base64.StdEncoding.EncodeToString(digest[:])
40 toSign = append(toSign, "digest")
41 fmt.Fprintf(hash, "digest: %s", d)
42 req.Header.Set("Digest", d)
44 sig, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, hash.Sum(nil))
48 bsig := base64.StdEncoding.EncodeToString(sig)
50 val := fmt.Sprintf("keyId=%q,algorithm=%q,headers=%q,signature=%q", pubkeyURL, "rsa-sha256", strings.Join(toSign, " "), bsig)
51 req.Header.Set("Signature", val)
55 type signature struct {
62 func parseSignatureHeader(line string) (signature, error) {
64 for _, v := range strings.Split(line, ",") {
65 name, val, ok := strings.Cut(v, "=")
67 return sig, fmt.Errorf("bad field: %s from %s", v, line)
69 val = strings.Trim(val, `"`)
80 return signature{}, fmt.Errorf("bad field name %s", name)
85 return sig, fmt.Errorf("missing signature field keyId")
86 } else if sig.algorithm == "" {
87 return sig, fmt.Errorf("missing signature field algorithm")
88 } else if sig.headers == "" {
89 return sig, fmt.Errorf("missing signature field headers")
90 } else if sig.signature == "" {
91 return sig, fmt.Errorf("missing signature field signature")