create: pkg algo/rs
pkg algo/rs подписывает данные с помощью ключа RSA
This commit is contained in:
parent
508713f53d
commit
fd8edfb70a
102
algo/rs/option.go
Normal file
102
algo/rs/option.go
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package rs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Option func(*Algo) error
|
||||||
|
|
||||||
|
// WithGenerateKey генерирует RSA ключ
|
||||||
|
func WithGenerateKey(bits int) Option {
|
||||||
|
return func(a *Algo) error {
|
||||||
|
key, err := rsa.GenerateKey(rand.Reader, bits)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
a.p = key
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithKey устанавливает приватный ключ
|
||||||
|
func WithKey(key *rsa.PrivateKey) Option {
|
||||||
|
return func(a *Algo) error {
|
||||||
|
a.p = key
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPub устанавливает публичный ключ
|
||||||
|
func WithPub(pub *rsa.PublicKey) Option {
|
||||||
|
return func(a *Algo) error {
|
||||||
|
if a.p == nil {
|
||||||
|
a.p = &rsa.PrivateKey{}
|
||||||
|
}
|
||||||
|
a.p.PublicKey = *pub
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithKeyPemFile загружает приватный ключ из pem-файла
|
||||||
|
func WithKeyPEMFile(file string) Option {
|
||||||
|
return func(a *Algo) error {
|
||||||
|
buf, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
block, _ := pem.Decode(buf)
|
||||||
|
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return WithKey(key)(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithPEM(pub, key []byte) Option {
|
||||||
|
return func(a *Algo) error {
|
||||||
|
if len(key) > 0 {
|
||||||
|
block, _ := pem.Decode(key)
|
||||||
|
val, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return WithKey(val)(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
block, _ := pem.Decode(pub)
|
||||||
|
val, err := x509.ParsePKCS1PublicKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return WithPub(val)(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPubFile загружает публичный ключ из pem-файла
|
||||||
|
func WithPubFile(file string) Option {
|
||||||
|
return func(a *Algo) error {
|
||||||
|
buf, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
block, _ := pem.Decode(buf)
|
||||||
|
pub, err := x509.ParsePKCS1PublicKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return WithPub(pub)(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
119
algo/rs/rs.go
Normal file
119
algo/rs/rs.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package rs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"git.daebt.dev/auth/algo"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
KeyRSA algo.KeyType = "RSA"
|
||||||
|
AlgorithmRS256 algo.AlgorithmType = "RS256"
|
||||||
|
AlgorithmRS384 algo.AlgorithmType = "RS384"
|
||||||
|
AlgorithmRS512 algo.AlgorithmType = "RS512"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Algo struct {
|
||||||
|
k algo.KeyType
|
||||||
|
a algo.AlgorithmType
|
||||||
|
h crypto.Hash
|
||||||
|
p *rsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Algo) Sign(payload []byte) ([]byte, error) {
|
||||||
|
val, err := a.hasher(payload)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rsa.SignPKCS1v15(rand.Reader, a.p, a.h, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Algo) Verify(payload, signature []byte) error {
|
||||||
|
val, err := a.hasher(payload)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if rsa.VerifyPKCS1v15(&a.p.PublicKey, a.h, val, signature) != nil {
|
||||||
|
return algo.ErrInvalidSignature
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Algo) Key() algo.KeyType {
|
||||||
|
return a.k
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Algo) Algo() algo.AlgorithmType {
|
||||||
|
return a.a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Algo) hasher(payload []byte) ([]byte, error) {
|
||||||
|
val := a.h.New()
|
||||||
|
|
||||||
|
if _, err := val.Write(payload); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return val.Sum(nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Algo) WriteKeyPEM(w io.Writer) error {
|
||||||
|
return pem.Encode(w, &pem.Block{
|
||||||
|
Type: "RSA PRIVATE KEY",
|
||||||
|
Bytes: x509.MarshalPKCS1PrivateKey(a.p),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Algo) WritePubPEM(w io.Writer) error {
|
||||||
|
return pem.Encode(w, &pem.Block{
|
||||||
|
Type: "RSA PUBLIC KEY",
|
||||||
|
Bytes: x509.MarshalPKCS1PublicKey(&a.p.PublicKey),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRS(a *Algo, o ...Option) (*Algo, error) {
|
||||||
|
for _, f := range o {
|
||||||
|
if err := f(a); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.p == nil {
|
||||||
|
return nil, errors.New("key is not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRS256(o ...Option) (*Algo, error) {
|
||||||
|
return newRS(&Algo{
|
||||||
|
k: KeyRSA,
|
||||||
|
a: AlgorithmRS256,
|
||||||
|
h: crypto.SHA256,
|
||||||
|
}, o...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRS384(o ...Option) (*Algo, error) {
|
||||||
|
return newRS(&Algo{
|
||||||
|
k: KeyRSA,
|
||||||
|
a: AlgorithmRS384,
|
||||||
|
h: crypto.SHA384,
|
||||||
|
}, o...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRS512(o ...Option) (*Algo, error) {
|
||||||
|
return newRS(&Algo{
|
||||||
|
k: KeyRSA,
|
||||||
|
a: AlgorithmRS512,
|
||||||
|
h: crypto.SHA512,
|
||||||
|
}, o...)
|
||||||
|
}
|
||||||
92
algo/rs/rs_test.go
Normal file
92
algo/rs/rs_test.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package rs_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.daebt.dev/auth/algo/rs"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pub = `-----BEGIN RSA PUBLIC KEY-----
|
||||||
|
MIIBCgKCAQEAzIWvl1OtwExnQ3HvoYk6bFRIlcCjzdv1yHazJfr6jxk6w+tCsIWE
|
||||||
|
dtKsAPm3gmmFG+mTuHq+H53sahm6DD9YC5ZQjnvSYkBKv70Zw331/tg9VLbfJc+g
|
||||||
|
N7kbD3xMQsucYD0973r7l9pEPH4Qw/I+BEKHMxlTmynStgKxnfyO6iPkL5jTzpUX
|
||||||
|
lD4V9xqUoMY/uX3EpGwbJJKFJuphYX3jzJQ++tovQGGep7RgNeMEoWjyAkJ2yb7t
|
||||||
|
iSWsw7qk6GO2z6NDmnc1UsdSBZ6Vg7BPUp8EINAdX1wbmB0+QH/vp0huM6lSY6NM
|
||||||
|
BugwptQUe5UCaly9fN4kb26U0qglEoGB6wIDAQAB
|
||||||
|
-----END RSA PUBLIC KEY-----`
|
||||||
|
|
||||||
|
var key = `-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEAzIWvl1OtwExnQ3HvoYk6bFRIlcCjzdv1yHazJfr6jxk6w+tC
|
||||||
|
sIWEdtKsAPm3gmmFG+mTuHq+H53sahm6DD9YC5ZQjnvSYkBKv70Zw331/tg9VLbf
|
||||||
|
Jc+gN7kbD3xMQsucYD0973r7l9pEPH4Qw/I+BEKHMxlTmynStgKxnfyO6iPkL5jT
|
||||||
|
zpUXlD4V9xqUoMY/uX3EpGwbJJKFJuphYX3jzJQ++tovQGGep7RgNeMEoWjyAkJ2
|
||||||
|
yb7tiSWsw7qk6GO2z6NDmnc1UsdSBZ6Vg7BPUp8EINAdX1wbmB0+QH/vp0huM6lS
|
||||||
|
Y6NMBugwptQUe5UCaly9fN4kb26U0qglEoGB6wIDAQABAoIBADU244UgRKkwN/4Y
|
||||||
|
ex0ws37UPz6XrQc3IDBUkjBjqSXqjpvDbsq3Mswn7JEkaFcKVZP5pnHtneJkGMtS
|
||||||
|
flIJeUMqjTNFjGv8Bnb1IOr4rzTr1qlgG5ee+jUFeMECumT2zW1NAfx5p1TPecmz
|
||||||
|
k3EoanJ5TOxCvro0m5q4ALb2q8jHrtfvtqEBrHepeEp3Lyh7m4ZUib+0yWXs0EPC
|
||||||
|
HhF9kLpCy+tVXUPDyLCt4cldTUda/3xeswzmxVRrHkt/idsNuTAi7o1Cx/OfZYMI
|
||||||
|
AzQo8OTh1Bg2DlXKtOX40frIxy3/K77F3ozwV4a3FravUO+wvcQdIyi6KAmNBFS3
|
||||||
|
9IVA7IECgYEA4x0TAn7vOvazILmg4Es0/gsWlh7RGmNTqVYtj5TfwOviUGeaculR
|
||||||
|
BSsyX8pgaROJKjGDcNzSQQEhHfJXrhNXeMJ0zPUIsJCBmih8oaCpAScWpV+qpdky
|
||||||
|
1Eb2akEg7XbpqBJJ1jnoEvIhd4feCAN8Gv8vcmdER7HaGdyef4XxlFsCgYEA5okG
|
||||||
|
tbyTtD2cfmYjYsoGqEfGH0Pe9vxc+MBthiPg0f2lpg+YuSPx92ZuJciLNyNWo9qf
|
||||||
|
NFnzbSEFxzomK/Bgq9ujGnbPyLOCadIADM4/njEEPe+IsagDxBgTrCEUJ56W9MLj
|
||||||
|
N+b4d/gnBkK4roDW8gjy7x4MbePByoDfaWtU/bECgYEApn8RCZpe7V4gMdSEIQph
|
||||||
|
fgBI/aL37p10nsbDvegJJRiIoCNjsexj7iMd2eW2SjH9M4Z68smgBfG7AoZASyh4
|
||||||
|
ztnX4M2eIjq+GHKn86GhZGvwiSoaI12YitC/I2Q9rHipkQJfSQLIpOMHL+bWGg/b
|
||||||
|
8rqzYO5duyWiW6VGOPzL/tMCgYB3JVSZcrfnzHvn+8PIF9+u80FbAUnn3m/yhAlW
|
||||||
|
7Y4RGYWWOLNW5FP26DJ/RpFk0tfBYYksllywBwQkflIiHV7pE1/NmqAy+0uog0dR
|
||||||
|
VvscN/sYQ4cjQlGH9GWebY4sF9Ou9lZWmwHJhzAsFSm7zozIlIVxvdbwqGiMz2Qn
|
||||||
|
6LgJUQKBgQC9H2JGm54wg0YPuDig5LjymUxYJrEiJT0IXz4vy+UEMxw+1EmeD5sm
|
||||||
|
kSqHkwNDp7D+3nik5HzoFVifJAvqFWU73fpvqQlvZSNfVrtq8UvJBIuH7eHkrJrC
|
||||||
|
L8dEn16HWjLX50GlT+9eYyHWtYI4sMdnzz1/JS6PwQRxKlFQN9HJYg==
|
||||||
|
-----END RSA PRIVATE KEY-----`
|
||||||
|
|
||||||
|
func TestMain(t *testing.T) {
|
||||||
|
data := []*struct {
|
||||||
|
a func(o ...rs.Option) (*rs.Algo, error)
|
||||||
|
o []rs.Option
|
||||||
|
f func(a *rs.Algo) error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
a: rs.NewRS256,
|
||||||
|
o: []rs.Option{
|
||||||
|
rs.WithPEM(nil, []byte(key)),
|
||||||
|
},
|
||||||
|
f: func(a *rs.Algo) error {
|
||||||
|
_, err := a.Sign([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
a: rs.NewRS256,
|
||||||
|
o: []rs.Option{
|
||||||
|
rs.WithPEM([]byte(pub), nil),
|
||||||
|
},
|
||||||
|
f: func(a *rs.Algo) error {
|
||||||
|
buf, err := hex.DecodeString("80e9b1c2a38cdb7c383a146597699e76a4b0c606e70eaec59998ff08168f2d51f487c625e30917fcbed75ff40ab1ee723599120fbbc9255d0ea442e0c9c8d76abcae7c5e8c6f56b9b07c30bf217538ce9ea073c4c2e1ef3a24b322a328175293e9b57c0c8d8e034d5e5ecdb059368114861730243af296aedf942d2a9f38d26599a5450845224907fa1599b792b81356bd53985401aaa0a3ad5f02e77d28008f181d3e3aafce4d40ebb770e70c8837c60e3eb307064bc49a2b35e3b73a155fbc490723ec7919aa9740f8c787b7586de1deed8480030bea44dc7ac4882a90a89596b78f58a41fd3e5e0269e6b75490e8e6e63717f0f67f54ba34593cc5520b93f")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||||
|
|
||||||
|
return a.Verify(p, buf)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range data {
|
||||||
|
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||||
|
val, err := v.a(v.o...)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
if err := v.f(val); err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user