golangの日記

Go言語を中心にプログラミングについてのブログ

Go言語(golang)でWSSE認証する

golang.png


某ブログサービスでコマンドラインからAPIで記事を投稿するのに、
WSSE認証とBasic認証の二択だったので書いてみました。

なにか書き方が間違ってるのか、たまにエラーになります。
結局、はてなブログにしたので、そこらへんは未検証のままです。

package main

import (
    "crypto/rand"
    "crypto/sha1"
    "encoding/base64"
    "fmt"
    "net/http"
    "time"
)

type WSSE struct {
    username  string
    password  string
    nonceSize int
}

func New(username, password string, nonceSize int) *WSSE {
    if nonceSize < 1 {
        nonceSize = 20
    }
    return &WSSE{
        username:  username,
        password:  password,
        nonceSize: nonceSize,
    }
}

func createNonce(size int) (string, error) {
    nonce := make([]byte, size)
    _, err := rand.Read(nonce)
    if err != nil {
        return "", err
    }
    return fmt.Sprintf("%x", nonce), nil
}

func (w *WSSE) Create() (string, error) {
    created := time.Now().Format(`2006-01-02T15:04:05Z`)

    n, err := createNonce(w.nonceSize)
    if err != nil {
        return "", err
    }

    h := sha1.New()
    h.Write([]byte(n))
    h.Write([]byte(created))
    h.Write([]byte(w.password))
    digest := base64.URLEncoding.EncodeToString(h.Sum(nil))
    nonce := base64.URLEncoding.EncodeToString([]byte(n))

    format := `UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s"`
    return fmt.Sprintf(format, w.username, digest, nonce, created), nil
}


func main() {
    w := New("username", "password", 14)
    
    req, err := http.NewRequest("GET", "https://example.com", nil)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    
    s, err := w.Create()
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    req.Header.Set("X-WSSE", s)
    
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}