Created
December 22, 2018 00:21
-
-
Save tom-code/5395ca572b0bab6c4ce52395e76018b4 to your computer and use it in GitHub Desktop.
acme experiment
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "fmt" | |
| "crypto/ecdsa" | |
| "crypto/elliptic" | |
| "crypto/rand" | |
| "crypto/x509" | |
| "os" | |
| "encoding/pem" | |
| "net/http" | |
| "encoding/json" | |
| "gopkg.in/square/go-jose.v2" | |
| "strings" | |
| ) | |
| func writeKey(path string, k *ecdsa.PrivateKey) error { | |
| f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) | |
| if err != nil { | |
| return err | |
| } | |
| bytes, err := x509.MarshalECPrivateKey(k) | |
| if err != nil { | |
| return err | |
| } | |
| b := &pem.Block{Type: "EC PRIVATE KEY", Bytes: bytes} | |
| if err := pem.Encode(f, b); err != nil { | |
| f.Close() | |
| return err | |
| } | |
| return f.Close() | |
| } | |
| type Directory struct { | |
| NewAccount string | |
| NewNonce string | |
| NewOrder string | |
| } | |
| func discover(url string) *Directory { | |
| resp, err := http.Get(url) | |
| if err != nil { | |
| panic(err) | |
| } | |
| defer resp.Body.Close() | |
| if resp.StatusCode != 200 { | |
| fmt.Printf("can't get %s\n", url) | |
| fmt.Println(resp) | |
| return nil | |
| } | |
| var js struct { | |
| NewAccount string `json:"newAccount"` | |
| NewNonce string `json:"newNonce"` | |
| NewOrder string `json:"newOrder"` | |
| RevokeCert string `json:"revokeCert"` | |
| Meta struct { | |
| Terms string `json:"termsOfService"` | |
| } | |
| } | |
| decoder := json.NewDecoder(resp.Body) | |
| err = decoder.Decode(&js) | |
| if err != nil { | |
| panic(err) | |
| } | |
| return &Directory{NewAccount: js.NewAccount, NewNonce: js.NewNonce, NewOrder: js.NewOrder} | |
| } | |
| func getNonce(url string) string { | |
| resp, err := http.Get(url) | |
| if err != nil { | |
| panic(err) | |
| } | |
| resp.Body.Close() | |
| return resp.Header.Get("replay-nonce") | |
| } | |
| type Order struct { | |
| Authorizations []string | |
| FinalizeUrl string | |
| OrderUrl string | |
| } | |
| func parseOrderResponse(resp *http.Response) *Order { | |
| orderLocation := resp.Header.Get("Location") | |
| fmt.Printf("order location %s\n", orderLocation) | |
| var js struct { | |
| Identifiers []struct { | |
| Type string `json:"type"` | |
| Value string `json:"value"` | |
| } `json:"Identifiers"` | |
| Authorizations []string `json:"authorizations"` | |
| Finalize string `json:"finalize"` | |
| } | |
| decoder := json.NewDecoder(resp.Body) | |
| err := decoder.Decode(&js) | |
| if err != nil { | |
| panic(err) | |
| } | |
| return &Order {Authorizations: js.Authorizations, FinalizeUrl: js.Finalize, OrderUrl: orderLocation} | |
| } | |
| type AuthChallenge struct { | |
| Type string | |
| Url string | |
| Token string | |
| } | |
| func parseAuthzChalenge(resp *http.Response) *AuthChallenge { | |
| var js struct { | |
| Challenges []struct { | |
| Type string `json:"type"` | |
| Url string `json:"url"` | |
| Token string `json:"token"` | |
| } `json:"challenges"` | |
| } | |
| decoder := json.NewDecoder(resp.Body) | |
| err := decoder.Decode(&js) | |
| if err != nil { | |
| panic(err) | |
| } | |
| fmt.Println(js) | |
| for _, ch := range(js.Challenges) { | |
| if ch.Type == "http-01" { | |
| return &AuthChallenge{Type: ch.Type, Url: ch.Url, Token: ch.Token} | |
| } | |
| } | |
| return nil | |
| } | |
| type NS struct { | |
| nonce string | |
| } | |
| func (ns NS)Nonce()(string, error) { | |
| return ns.nonce, nil | |
| } | |
| func postJWS(key *ecdsa.PrivateKey, url string, payload string, nonce string, kid string) *http.Response { | |
| jsonWebKey := jose.JSONWebKey{ | |
| Key: key, | |
| //KeyID: kid, | |
| Algorithm: string(jose.ES256), | |
| } | |
| options := &jose.SignerOptions{} | |
| options.WithHeader("url", url) | |
| if len(kid) > 0 { | |
| options.WithHeader("kid", kid) | |
| } else { | |
| options.EmbedJWK = true | |
| } | |
| options.NonceSource = NS{nonce} | |
| signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.ES256, Key: jsonWebKey}, options) | |
| if err != nil { | |
| panic(err) | |
| } | |
| fmt.Println(signer) | |
| jws, err := signer.Sign([]byte(payload)) | |
| if err != nil { | |
| fmt.Println(err.Error()) | |
| } | |
| output := jws.FullSerialize() | |
| fmt.Println(output) | |
| res, err := http.Post(url, "application/jose+json", strings.NewReader(output)) | |
| return res | |
| } | |
| func main() { | |
| fmt.Println("generating private key...") | |
| ecKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) | |
| if err != nil { | |
| panic(err) | |
| } | |
| writeKey("key.pem", ecKey) | |
| directory := discover("http://localhost:14000/dir") | |
| fmt.Println(directory) | |
| nonce := getNonce(directory.NewNonce) | |
| fmt.Printf("nonce=%s\n", nonce) | |
| // create new account | |
| newAcc := `{"termsOfServiceAgreed":true,"contact":["mailto:a@c.s"]}` | |
| res := postJWS(ecKey, directory.NewAccount, newAcc, nonce, "") | |
| account := res.Header.Get("Location") | |
| nonce = res.Header.Get("replay-nonce") | |
| res.Body.Close() | |
| fmt.Println(account) | |
| // start certificate order | |
| newOrder := `{"identifiers": [ { "type": "dns", "value": "example.com.a" } ]}` | |
| res = postJWS(ecKey, directory.NewOrder, newOrder, nonce, account) | |
| nonce = res.Header.Get("replay-nonce") | |
| order := parseOrderResponse(res) | |
| res.Body.Close() | |
| fmt.Println(order) | |
| // start authorization | |
| res = postJWS(ecKey, order.Authorizations[0], "", nonce, account) | |
| challenge := parseAuthzChalenge(res) | |
| nonce = res.Header.Get("replay-nonce") | |
| res.Body.Close() | |
| // confirm we arranged resource | |
| res = postJWS(ecKey, challenge.Url, "{}", nonce, account) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment