File: inspect.go

package info (click to toggle)
golang-github-smallstep-cli 0.15.16%2Bds-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 4,404 kB
  • sloc: sh: 512; makefile: 99
file content (171 lines) | stat: -rw-r--r-- 4,477 bytes parent folder | download | duplicates (2)
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
package key

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/ed25519"
	"crypto/rsa"
	"fmt"
	"math/big"

	"github.com/pkg/errors"
	"github.com/smallstep/cli/command"
	"github.com/smallstep/cli/crypto/pemutil"
	"github.com/smallstep/cli/errs"
	"github.com/smallstep/cli/utils"
	"github.com/urfave/cli"
)

func inspectCommand() cli.Command {
	return cli.Command{
		Name:      "inspect",
		Action:    command.ActionFunc(inspectAction),
		Usage:     `print key details in human readable format`,
		UsageText: `**step crypto key inspect** <key-file>`,
		Description: `**step crypto key inspect** prints details of a public or a private key in a
human readable format the public key corresponding to the given <key-file>.

## POSITIONAL ARGUMENTS

<key-file>
:  Path to a public or private key.

## EXAMPLES

Print details of the given key:
'''
$ step crypto key inspect priv.pem
'''

## NOTES

This command shows the raw parameters of the keys, it does not include headers
that the marshaled version of the keys might have. For example, a marshaled
version an EC public key will have 0x04 in the first byte to indicate the
uncompressed form specified in section 4.3.6 of ANSI X9.62.`,
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "password-file",
				Usage: "The path to the <file> containing passphrase to decrypt private key.",
			},
		},
	}
}

func inspectAction(ctx *cli.Context) error {
	if err := errs.MinMaxNumberOfArguments(ctx, 0, 1); err != nil {
		return err
	}

	var name string
	switch ctx.NArg() {
	case 0:
		name = "-"
	case 1:
		name = ctx.Args().First()
	default:
		return errs.TooManyArguments(ctx)
	}

	b, err := utils.ReadFile(name)
	if err != nil {
		return err
	}

	var key interface{}
	switch {
	case bytes.HasPrefix(b, []byte("-----BEGIN ")):
		opts := []pemutil.Options{
			pemutil.WithFilename(name),
			pemutil.WithFirstBlock(),
		}
		if passFile := ctx.String("password-file"); passFile != "" {
			opts = append(opts, pemutil.WithPasswordFile(passFile))
		}
		if key, err = pemutil.ParseKey(b, opts...); err != nil {
			return err
		}
	case isSSHPublicKey(b):
		if key, err = pemutil.ParseSSH(b); err != nil {
			return err
		}
	case isJWK(b):
		if key, err = parseJWK(ctx, b); err != nil {
			return err
		}
	default: // assume DER format
		if key, err = pemutil.ParseDER(b); err != nil {
			return err
		}
	}

	switch k := key.(type) {
	case *rsa.PublicKey:
		fmt.Printf("RSA Public-Key: (%d bit)\n", k.Size()*8)
		bigIntPaddedPrinter("Modulus", k.N, k.Size())
		fmt.Printf("Exponent: %d (0x%x)\n", k.E, k.E)
	case *rsa.PrivateKey:
		fmt.Printf("RSA Private-Key: (%d bit)\n", k.Size()*8)
		bigIntPaddedPrinter("Modulus", k.N, k.Size())
		fmt.Printf("Public Exponent: %d (0x%x)\n", k.E, k.E)
		bigIntPaddedPrinter("Private Exponent", k.D, k.Size())
		for i := range k.Primes {
			bigIntPrinter(fmt.Sprintf("Prime #%d", i+1), k.Primes[i])
		}
		bigIntPrinter("Exponent #1", k.Precomputed.Dp)
		bigIntPrinter("Exponent #2", k.Precomputed.Dq)
		bigIntPrinter("Coefficient", k.Precomputed.Qinv)
	case *ecdsa.PublicKey:
		byteLen := (k.Params().BitSize + 7) >> 3
		fmt.Printf("EC Public-Key: (%d bit)\n", k.Params().BitSize)
		bigIntPaddedPrinter("X", k.X, byteLen)
		bigIntPaddedPrinter("Y", k.Y, byteLen)
		fmt.Printf("Curve: %s\n", k.Params().Name)
	case *ecdsa.PrivateKey:
		byteLen := (k.Params().BitSize + 7) >> 3
		fmt.Printf("EC PrivateKey-Key: (%d bit)\n", k.Params().BitSize)
		bigIntPaddedPrinter("X", k.X, byteLen)
		bigIntPaddedPrinter("Y", k.Y, byteLen)
		bigIntPaddedPrinter("D", k.D, byteLen)
		fmt.Printf("Curve: %s\n", k.Params().Name)
	case ed25519.PublicKey:
		fmt.Printf("Ed25519 Public-Key: (%d bit)\n", 8*len(k))
		bytesPrinter("Public", k)
	case ed25519.PrivateKey:
		fmt.Printf("Ed25519 Private-Key: (%d bit)\n", 8*len(k))
		bytesPrinter("Public", k[32:])
		bytesPrinter("Private", k[:32])
	default:
		return errors.Errorf("unsupported key type '%T'", k)
	}

	return nil
}

func bigIntPrinter(name string, val *big.Int) {
	bytesPrinter(name, val.Bytes())
}

func bigIntPaddedPrinter(name string, val *big.Int, size int) {
	bytesPrinter(name, paddedBytes(val.Bytes(), size))
}

func bytesPrinter(name string, bytes []byte) {
	fmt.Print(name + ":")
	for i, b := range bytes {
		if (i % 16) == 0 {
			fmt.Print("\n    ")
		}
		fmt.Printf("%02x", b)
		if i != len(bytes)-1 {
			fmt.Print(":")
		}
	}
	fmt.Println()
}

func paddedBytes(b []byte, size int) []byte {
	ret := make([]byte, size)
	copy(ret[len(ret)-len(b):], b)
	return ret
}