-
-
Notifications
You must be signed in to change notification settings - Fork 152
/
rsa.go
144 lines (119 loc) · 3.59 KB
/
rsa.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package sign
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha1" // nolint:gosec
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io"
"os"
)
var (
errNoPemBlock = errors.New("no PEM block found")
errDigestNotSH1 = errors.New("digest is not a SHA1 hash")
errNoPassphrase = errors.New("key is encrypted but no passphrase was provided")
errNoRSAKey = errors.New("key is not an RSA key")
)
const (
PKCS1PrivkeyPreamble = "RSA PRIVATE KEY"
PKCS8PrivkeyPreamble = "PRIVATE KEY"
)
// RSASignSHA1Digest signs the provided SHA1 message digest. The key file
// must be in the PEM format and can either be encrypted or not.
func RSASignSHA1Digest(sha1Digest []byte, keyFile, passphrase string) ([]byte, error) {
if len(sha1Digest) != sha1.Size {
return nil, errDigestNotSH1
}
keyFileContent, err := os.ReadFile(keyFile)
if err != nil {
return nil, fmt.Errorf("reading key file: %w", err)
}
block, _ := pem.Decode(keyFileContent)
if block == nil {
return nil, errNoPemBlock
}
blockData := block.Bytes
if x509.IsEncryptedPEMBlock(block) { //nolint:staticcheck
if passphrase == "" {
return nil, errNoPassphrase
}
var decryptedBlockData []byte
decryptedBlockData, err = x509.DecryptPEMBlock(block, []byte(passphrase)) //nolint:staticcheck
if err != nil {
return nil, fmt.Errorf("decrypt private key PEM block: %w", err)
}
blockData = decryptedBlockData
}
var priv crypto.Signer
switch block.Type {
case PKCS1PrivkeyPreamble:
priv, err = x509.ParsePKCS1PrivateKey(blockData)
if err != nil {
return nil, fmt.Errorf("parse PKCS#1 private key: %w", err)
}
case PKCS8PrivkeyPreamble:
privAny, err := x509.ParsePKCS8PrivateKey(blockData)
if err != nil {
return nil, fmt.Errorf("parse PKCS#8 private key: %w", err)
}
privTmp, ok := privAny.(crypto.Signer)
if !ok {
return nil, fmt.Errorf("cannot sign with given private key")
}
priv = privTmp
default:
return nil, fmt.Errorf(`key type "%v" is not supported`, block.Type)
}
signature, err := priv.Sign(rand.Reader, sha1Digest, crypto.SHA1)
if err != nil {
return nil, fmt.Errorf("signing: %w", err)
}
return signature, nil
}
func rsaSign(message io.Reader, keyFile, passphrase string) ([]byte, error) {
sha1Hash := sha1.New() // nolint:gosec
_, err := io.Copy(sha1Hash, message)
if err != nil {
return nil, fmt.Errorf("create SHA1 message digest: %w", err)
}
return RSASignSHA1Digest(sha1Hash.Sum(nil), keyFile, passphrase)
}
// RSAVerifySHA1Digest is exported for use in tests and verifies a signature over the
// provided SHA1 hash of a message. The key file must be in the PEM format.
func RSAVerifySHA1Digest(sha1Digest, signature []byte, publicKeyFile string) error {
if len(sha1Digest) != sha1.Size {
return errDigestNotSH1
}
keyFileContent, err := os.ReadFile(publicKeyFile)
if err != nil {
return fmt.Errorf("reading key file: %w", err)
}
block, _ := pem.Decode(keyFileContent)
if block == nil {
return errNoPemBlock
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return fmt.Errorf("parse PKIX public key: %w", err)
}
rsaPub, ok := pub.(*rsa.PublicKey)
if !ok {
return errNoRSAKey
}
err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA1, sha1Digest, signature)
if err != nil {
return fmt.Errorf("verify PKCS1v15 signature: %w", err)
}
return nil
}
func rsaVerify(message io.Reader, signature []byte, publicKeyFile string) error {
sha1Hash := sha1.New() // nolint:gosec
_, err := io.Copy(sha1Hash, message)
if err != nil {
return fmt.Errorf("create SHA1 message digest: %w", err)
}
return RSAVerifySHA1Digest(sha1Hash.Sum(nil), signature, publicKeyFile)
}