185 lines
4.2 KiB
Go
185 lines
4.2 KiB
Go
package api
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
|
|
"git.coolaj86.com/coolaj86/go-mockid/xkeypairs"
|
|
"git.rootprojects.org/root/keypairs"
|
|
)
|
|
|
|
// RandomReader may be overwritten for testing
|
|
var RandomReader io.Reader = rand.Reader
|
|
|
|
// GeneratePublicJWK will create a new private key in JWK format
|
|
func GeneratePublicJWK(w http.ResponseWriter, r *http.Request) {
|
|
if "POST" != r.Method {
|
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
opts, err := getOpts(r)
|
|
if nil != err {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
privkey, err := getPrivKey(opts)
|
|
if nil != err {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
jwk := keypairs.MarshalJWKPublicKey(keypairs.NewPublicKey(privkey.Public()))
|
|
w.Write(append(jwk, '\n'))
|
|
}
|
|
|
|
// GeneratePrivateJWK will create a new private key in JWK format
|
|
func GeneratePrivateJWK(w http.ResponseWriter, r *http.Request) {
|
|
if "POST" != r.Method {
|
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
opts, err := getOpts(r)
|
|
if nil != err {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
privkey := genPrivKey(opts)
|
|
|
|
jwk := xkeypairs.MarshalJWKPrivateKey(privkey)
|
|
w.Write(append(jwk, '\n'))
|
|
}
|
|
|
|
// GeneratePublicDER will create a new private key in JWK format
|
|
func GeneratePublicDER(w http.ResponseWriter, r *http.Request) {
|
|
if "POST" != r.Method {
|
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
opts, err := getOpts(r)
|
|
if nil != err {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
privkey, err := getPrivKey(opts)
|
|
if nil != err {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
b, _ := xkeypairs.MarshalDERPublicKey(privkey.Public())
|
|
|
|
w.Write(b)
|
|
}
|
|
|
|
// GeneratePrivateDER will create a new private key in a valid DER encoding
|
|
func GeneratePrivateDER(w http.ResponseWriter, r *http.Request) {
|
|
if "POST" != r.Method {
|
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
opts, err := getOpts(r)
|
|
if nil != err {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
privkey := genPrivKey(opts)
|
|
|
|
der, _ := xkeypairs.MarshalDERPrivateKey(privkey)
|
|
w.Write(der)
|
|
}
|
|
|
|
// GeneratePublicPEM will create a new private key in JWK format
|
|
func GeneratePublicPEM(w http.ResponseWriter, r *http.Request) {
|
|
if "POST" != r.Method {
|
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
opts, err := getOpts(r)
|
|
if nil != err {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
privkey, err := getPrivKey(opts)
|
|
if nil != err {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
b, _ := xkeypairs.MarshalPEMPublicKey(privkey.Public())
|
|
|
|
w.Write(b)
|
|
}
|
|
|
|
// GeneratePrivatePEM will create a new private key in a valid PEM encoding
|
|
func GeneratePrivatePEM(w http.ResponseWriter, r *http.Request) {
|
|
if "POST" != r.Method {
|
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
opts, err := getOpts(r)
|
|
if nil != err {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
privkey := genPrivKey(opts)
|
|
|
|
privpem, _ := xkeypairs.MarshalPEMPrivateKey(privkey)
|
|
w.Write(privpem)
|
|
}
|
|
|
|
const maxRetry = 16
|
|
|
|
func getPrivKey(opts *options) (keypairs.PrivateKey, error) {
|
|
if "" != opts.Key {
|
|
return keypairs.ParsePrivateKey([]byte(opts.Key))
|
|
}
|
|
return genPrivKey(opts), nil
|
|
}
|
|
|
|
func genPrivKey(opts *options) keypairs.PrivateKey {
|
|
var privkey keypairs.PrivateKey
|
|
|
|
if "RSA" == opts.KeyType {
|
|
keylen := 2048
|
|
privkey, _ = rsa.GenerateKey(opts.nextReader(), keylen)
|
|
if 0 != opts.Seed {
|
|
for i := 0; i < maxRetry; i++ {
|
|
otherkey, _ := rsa.GenerateKey(opts.nextReader(), keylen)
|
|
otherCmp := otherkey.D.Cmp(privkey.(*rsa.PrivateKey).D)
|
|
if 0 != otherCmp {
|
|
// There are two possible keys, choose the lesser D value
|
|
// See https://github.com/square/go-jose/issues/189
|
|
if otherCmp < 0 {
|
|
privkey = otherkey
|
|
}
|
|
break
|
|
}
|
|
if maxRetry == i-1 {
|
|
log.Printf("error: coinflip landed on heads %d times", maxRetry)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// TODO: EC keys may also suffer the same random problems in the future
|
|
privkey, _ = ecdsa.GenerateKey(elliptic.P256(), opts.nextReader())
|
|
}
|
|
return privkey
|
|
}
|