зеркало из https://github.com/github/smimesign.git
implement --list-keys command
This commit is contained in:
Родитель
d0d0da8687
Коммит
5abdd89a0b
|
@ -0,0 +1,37 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func commandListKeys() int {
|
||||
idents, err := store.Identities()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, ident := range idents {
|
||||
defer ident.Close()
|
||||
}
|
||||
|
||||
for j, ident := range idents {
|
||||
cert, err := ident.Certificate()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if j > 0 {
|
||||
fmt.Println("————————————————————")
|
||||
}
|
||||
|
||||
fmt.Println(" ID:", certHexFingerprint(cert))
|
||||
fmt.Println(" S/N:", cert.SerialNumber.Text(16))
|
||||
fmt.Println("Algorithm:", cert.SignatureAlgorithm.String())
|
||||
fmt.Println(" Validity:", cert.NotBefore.String(), "-", cert.NotAfter.String())
|
||||
fmt.Println(" Issuer:", rdnSequenceString(cert.Issuer.ToRDNSequence()))
|
||||
fmt.Println(" Subject:", rdnSequenceString(cert.Subject.ToRDNSequence()))
|
||||
fmt.Println(" Emails:", strings.Join(certEmails(cert), ", "))
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
35
main.go
35
main.go
|
@ -10,16 +10,17 @@ import (
|
|||
|
||||
var (
|
||||
// Action flags
|
||||
helpFlag = getopt.BoolLong("help", 'h', "print this help message")
|
||||
signFlag = getopt.BoolLong("sign", 's', "make a signature")
|
||||
verifyFlag = getopt.BoolLong("verify", 0, "verify a signature")
|
||||
helpFlag = getopt.BoolLong("help", 'h', "print this help message")
|
||||
signFlag = getopt.BoolLong("sign", 's', "make a signature")
|
||||
verifyFlag = getopt.BoolLong("verify", 0, "verify a signature")
|
||||
listKeysFlag = getopt.BoolLong("list-keys", 0, "show keys")
|
||||
|
||||
// Option flags
|
||||
localUserOpt = getopt.StringLong("local-user", 'u', "", "use USER-ID to sign", "USER-ID")
|
||||
detachSignFlag = getopt.BoolLong("detach-sign", 'b', "make a detached signature")
|
||||
armorFlag = getopt.BoolLong("armor", 'a', "create ascii armored output")
|
||||
statusFdOpt = getopt.IntLong("status-fd", 0, -1, "Write special status strings to the file descriptor n.", "n")
|
||||
keyFormatOpt = getopt.EnumLong("keyid-format", 0, []string{"short", "0xshort", "long", "0xlong"}, "short", "Select how to display key IDs.", "{short|0xshort|long|0xlong}")
|
||||
keyFormatOpt = getopt.EnumLong("keyid-format", 0, []string{"long"}, "long", "Select how to display key IDs.", "{long}")
|
||||
fileArgs []string
|
||||
|
||||
store certstore.Store
|
||||
|
@ -41,23 +42,23 @@ func main() {
|
|||
|
||||
status := 1
|
||||
if *helpFlag {
|
||||
if *signFlag || *verifyFlag {
|
||||
fmt.Println("specify --help, --sign, or --verify")
|
||||
if *signFlag || *verifyFlag || *listKeysFlag {
|
||||
fmt.Println("specify --help, --sign, --verify, or --list-keys")
|
||||
} else {
|
||||
getopt.Usage()
|
||||
status = 0
|
||||
}
|
||||
} else if *signFlag {
|
||||
if *helpFlag || *verifyFlag {
|
||||
fmt.Println("specify --help, --sign, or --verify")
|
||||
if *helpFlag || *verifyFlag || *listKeysFlag {
|
||||
fmt.Println("specify --help, --sign, --verify, or --list-keys")
|
||||
} else if len(*localUserOpt) == 0 {
|
||||
fmt.Println("specify a USER-ID to sign with")
|
||||
} else {
|
||||
status = commandSign()
|
||||
}
|
||||
} else if *verifyFlag {
|
||||
if *helpFlag || *signFlag {
|
||||
fmt.Println("specify --help, --sign, or --verify")
|
||||
if *helpFlag || *signFlag || *listKeysFlag {
|
||||
fmt.Println("specify --help, --sign, --verify, or --list-keys")
|
||||
} else if len(*localUserOpt) > 0 {
|
||||
fmt.Println("local-user cannot be specified for verification")
|
||||
} else if *detachSignFlag {
|
||||
|
@ -67,8 +68,20 @@ func main() {
|
|||
} else {
|
||||
status = commandVerify()
|
||||
}
|
||||
} else if *listKeysFlag {
|
||||
if *helpFlag || *signFlag || *verifyFlag {
|
||||
fmt.Println("specify --help, --sign, --verify, or --list-keys")
|
||||
} else if len(*localUserOpt) > 0 {
|
||||
fmt.Println("local-user cannot be specified for list-keys")
|
||||
} else if *detachSignFlag {
|
||||
fmt.Println("detach-sign cannot be specified for list-keys")
|
||||
} else if *armorFlag {
|
||||
fmt.Println("armor cannot be specified for list-keys")
|
||||
} else {
|
||||
status = commandListKeys()
|
||||
}
|
||||
} else {
|
||||
fmt.Println("specify --help, --sign, or --verify")
|
||||
fmt.Println("specify --help, --sign, --verify, or --list-keys")
|
||||
}
|
||||
|
||||
os.Exit(status)
|
||||
|
|
|
@ -40,27 +40,28 @@ import (
|
|||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
var attributeTypeNames = map[string]string{
|
||||
"2.5.4.6": "C",
|
||||
"2.5.4.10": "O",
|
||||
"2.5.4.11": "OU",
|
||||
"2.5.4.3": "CN",
|
||||
"2.5.4.5": "SERIALNUMBER",
|
||||
"2.5.4.7": "L",
|
||||
"2.5.4.8": "ST",
|
||||
"2.5.4.9": "STREET",
|
||||
"2.5.4.17": "POSTALCODE",
|
||||
"2.5.4.6": "C",
|
||||
"2.5.4.10": "O",
|
||||
"2.5.4.11": "OU",
|
||||
"2.5.4.3": "CN",
|
||||
"2.5.4.5": "SERIALNUMBER",
|
||||
"2.5.4.7": "L",
|
||||
"2.5.4.8": "ST",
|
||||
"2.5.4.9": "STREET",
|
||||
"2.5.4.17": "POSTALCODE",
|
||||
"1.2.840.113549.1.9.1": "EMAIL",
|
||||
}
|
||||
|
||||
// The orignal code can be found at https://git.io/vbUMS
|
||||
//
|
||||
// String implements the fmt.Stringer interface. It loosely follows the
|
||||
// string conversion rules for Distinguished Names from RFC 2253.
|
||||
func RDNSequenceString(r pkix.RDNSequence) string {
|
||||
func rdnSequenceString(r pkix.RDNSequence) string {
|
||||
s := ""
|
||||
for i := 0; i < len(r); i++ {
|
||||
rdn := r[len(r)-1-i]
|
||||
if i > 0 {
|
||||
s += ","
|
||||
s += ", "
|
||||
}
|
||||
for j, tv := range rdn {
|
||||
if j > 0 {
|
||||
|
|
|
@ -190,14 +190,14 @@ func emitSigCreated(cert *x509.Certificate, isDetached bool) {
|
|||
}
|
||||
|
||||
func emitGoodSig(certs []*x509.Certificate) {
|
||||
subj := RDNSequenceString(certs[0].Subject.ToRDNSequence())
|
||||
subj := rdnSequenceString(certs[0].Subject.ToRDNSequence())
|
||||
fpr := certHexFingerprint(certs[0])
|
||||
|
||||
sGoodSig.emitf("%s %s", fpr, subj)
|
||||
}
|
||||
|
||||
func emitBadSig(certs []*x509.Certificate) {
|
||||
subj := RDNSequenceString(certs[0].Subject.ToRDNSequence())
|
||||
subj := rdnSequenceString(certs[0].Subject.ToRDNSequence())
|
||||
fpr := certHexFingerprint(certs[0])
|
||||
|
||||
sBadSig.emitf("%s %s", fpr, subj)
|
||||
|
|
42
utils.go
42
utils.go
|
@ -6,6 +6,7 @@ import (
|
|||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/hex"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -75,27 +76,34 @@ var (
|
|||
// certHasEmail checks if a certificate contains the given email address in its
|
||||
// subject (CN/emailAddress) or SAN fields.
|
||||
func certHasEmail(cert *x509.Certificate, email string) bool {
|
||||
if len(email) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check SAN
|
||||
for _, other := range cert.EmailAddresses {
|
||||
for _, other := range certEmails(cert) {
|
||||
if other == email {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Check CN and emailAddress fields in cert subject.
|
||||
for _, name := range cert.Subject.Names {
|
||||
if !name.Type.Equal(oidEmailAddress) && !name.Type.Equal(oidCommonName) {
|
||||
continue
|
||||
}
|
||||
|
||||
if other, isStr := name.Value.(string); isStr && other == email {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// borrowed from http://emailregex.com/
|
||||
var emailRegexp = regexp.MustCompile(`(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)`)
|
||||
|
||||
// certEmails extracts email addresses from a certificate's subject
|
||||
// (CN/emailAddress) and SAN extensions.
|
||||
func certEmails(cert *x509.Certificate) []string {
|
||||
// From SAN
|
||||
emails := cert.EmailAddresses
|
||||
|
||||
// From CN and emailAddress fields in subject.
|
||||
for _, name := range cert.Subject.Names {
|
||||
if !name.Type.Equal(oidEmailAddress) && !name.Type.Equal(oidCommonName) {
|
||||
continue
|
||||
}
|
||||
|
||||
if email, isStr := name.Value.(string); isStr && emailRegexp.MatchString(email) {
|
||||
emails = append(emails, email)
|
||||
}
|
||||
}
|
||||
|
||||
return emails
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче