Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to create keys from certificate private keys #5

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ func main() {
}
}

// init an empty public key with version 3
outKey := putty.Key{Version: 3}

// set the private key
outKey.SetKey(privateKey)

// print out the ppk file
fmt.Printf("%s\n", outKey.Marshal())

log.Printf("%+#v", privateKey)
}
```
38 changes: 35 additions & 3 deletions dsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func (k Key) readDSAPublicKey() (*dsa.PublicKey, error) {
G *big.Int
Pub *big.Int
}
err := unmarshal(k.PublicKey, &pub, false)
_, err := unmarshal(k.PublicKey, &pub, false)
if err != nil {
return nil, err
}
Expand All @@ -35,14 +35,32 @@ func (k Key) readDSAPublicKey() (*dsa.PublicKey, error) {
return publicKey, nil
}

func (k Key) readDSAPrivateKey() (*dsa.PrivateKey, error) {
func (k *Key) setDSAPublicKey(toSet *dsa.PublicKey) (err error) {
var pub struct {
Header string
P *big.Int
Q *big.Int
G *big.Int
Pub *big.Int
}
k.Algo = "ssh-dss"
pub.Header = k.Algo
pub.P = toSet.Parameters.P
pub.Q = toSet.Parameters.Q
pub.G = toSet.Parameters.G
pub.Pub = toSet.Y
k.PublicKey, err = marshal(&pub)
return
}

func (k *Key) readDSAPrivateKey() (*dsa.PrivateKey, error) {
publicKey, err := k.readDSAPublicKey()
if err != nil {
return nil, err
}

var priv *big.Int
err = unmarshal(k.PrivateKey, &priv, k.Encryption != "none")
k.keySize, err = unmarshal(k.PrivateKey, &priv, k.padded)
if err != nil {
return nil, err
}
Expand All @@ -54,3 +72,17 @@ func (k Key) readDSAPrivateKey() (*dsa.PrivateKey, error) {

return privateKey, nil
}

func (k *Key) setDSAPrivateKey(pk *dsa.PrivateKey) (err error) {
err = k.setDSAPublicKey(&pk.PublicKey)
if err != nil {
return err
}

var priv *big.Int
priv = pk.X
k.PrivateKey, err = marshal(&priv)
k.keySize = len(k.PrivateKey)
k.padded = false
return
}
55 changes: 52 additions & 3 deletions ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func (k Key) readECDSAPublicKey() (*ecdsa.PublicKey, error) {
Length string
Bytes []byte
}
err := unmarshal(k.PublicKey, &pub, false)
_, err := unmarshal(k.PublicKey, &pub, false)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -52,14 +52,52 @@ func (k Key) readECDSAPublicKey() (*ecdsa.PublicKey, error) {
return publicKey, nil
}

func (k Key) readECDSAPrivateKey() (*ecdsa.PrivateKey, error) {
func (k *Key) setECDSAPublicKey(pk *ecdsa.PublicKey) (err error) {
var pub struct {
Header string
Length string
Bytes []byte
}

switch c := pk.Curve.Params().Name; c {
case "P-256":
pub.Length = "nistp256"
case "P-384":
pub.Length = "nistp384"
case "P-521":
pub.Length = "nistp521"
default:
return fmt.Errorf("unsupported elliptic curve %s", c)
}

pub.Header = "ecdsa-sha2-" + pub.Length
k.Algo = pub.Header

x := (pk.X).Bytes()
y := (pk.Y).Bytes()

// balance the integers slices
if diff := len(x) - len(y); diff > 0 {
y = append(make([]byte, diff), y...)
} else if diff < 0 {
x = append(make([]byte, -diff), x...)
}

pub.Bytes = append([]byte{4}, x...)
pub.Bytes = append(pub.Bytes, y...)

k.PublicKey, err = marshal(&pub)
return
}

func (k *Key) readECDSAPrivateKey() (*ecdsa.PrivateKey, error) {
publicKey, err := k.readECDSAPublicKey()
if err != nil {
return nil, err
}

var priv *big.Int
err = unmarshal(k.PrivateKey, &priv, k.Encryption != "none")
k.keySize, err = unmarshal(k.PrivateKey, &priv, k.padded)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -87,3 +125,14 @@ func (k Key) readECDSAPrivateKey() (*ecdsa.PrivateKey, error) {

return privateKey, nil
}

func (k *Key) setECDSAPrivateKey(pk *ecdsa.PrivateKey) (err error) {
err = k.setECDSAPublicKey(&pk.PublicKey)
if err != nil {
return
}
k.PrivateKey, err = marshal(&pk.D)
k.keySize = len(k.PrivateKey)
k.padded = false
return
}
34 changes: 31 additions & 3 deletions ed25519.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ func (k Key) readED25519PublicKey() (*ed25519.PublicKey, error) {
Header string
Bytes []byte
}
err := unmarshal(k.PublicKey, &pub, false)
_, err := unmarshal(k.PublicKey, &pub, false)
if err != nil {
return nil, err
}
Expand All @@ -26,14 +26,26 @@ func (k Key) readED25519PublicKey() (*ed25519.PublicKey, error) {
return (*ed25519.PublicKey)(&pub.Bytes), nil
}

func (k Key) readED25519PrivateKey() (*ed25519.PrivateKey, error) {
func (k *Key) setED25519PublicKey(pk *ed25519.PublicKey) (err error) {
var pub struct {
Header string
Bytes []byte
}
k.Algo = "ssh-ed25519"
pub.Header = k.Algo
pub.Bytes = ([]byte)(*pk)
k.PublicKey, err = marshal(&pub)
return
}

func (k *Key) readED25519PrivateKey() (*ed25519.PrivateKey, error) {
publicKey, err := k.readED25519PublicKey()
if err != nil {
return nil, err
}

var priv []byte
err = unmarshal(k.PrivateKey, &priv, k.Encryption != "none")
k.keySize, err = unmarshal(k.PrivateKey, &priv, k.padded)
if err != nil {
return nil, err
}
Expand All @@ -48,3 +60,19 @@ func (k Key) readED25519PrivateKey() (*ed25519.PrivateKey, error) {

return &privateKey, nil
}

func (k *Key) setED25519PrivateKey(pk *ed25519.PrivateKey) (err error) {
bytes := ([]byte)(*pk)
cut := ed25519.PrivateKeySize - ed25519.PublicKeySize
pub := bytes[cut:]
err = k.setED25519PublicKey((*ed25519.PublicKey)(&pub))
if err != nil {
return err
}

priv := bytes[:cut]
k.PrivateKey, err = marshal(&priv)
k.keySize = len(k.PrivateKey)
k.padded = false
return
}
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ module github.com/kayrus/putty

go 1.13

require golang.org/x/crypto v0.1.0
require (
github.com/pschou/go-cbc3 v0.0.0-20221001011156-4e1428fbe50c // indirect
golang.org/x/crypto golang.org/x/crypto v0.1.0
)
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
github.com/pschou/go-cbc3 v0.0.0-20220927161413-29a957308e2d/go.mod h1:b5sRQXy0ZdH7/nBD+FLWT4W+R4OIgNTw4rM9o/+XeKc=
github.com/pschou/go-cbc3 v0.0.0-20221001011156-4e1428fbe50c h1:dk8hPEU14qkDRAfX/Lzt0N63I/2iW+vYlZecK7Vkc+4=
github.com/pschou/go-cbc3 v0.0.0-20221001011156-4e1428fbe50c/go.mod h1:e5fPLORCNq9grPyodp74EPO3uxyrZUEjXWCErhGMlLI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
Expand Down
15 changes: 15 additions & 0 deletions lib.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package putty

func splitByWidth(str string, size int) []string {
strLength := len(str)
var splited []string
var stop int
for i := 0; i < strLength; i += size {
stop = i + size
if stop > strLength {
stop = strLength
}
splited = append(splited, str[i:stop])
}
return splited
}
126 changes: 126 additions & 0 deletions marshal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package putty

import (
"bytes"
"crypto/aes"
"crypto/sha1"
"encoding/binary"
"fmt"
"math/big"
"reflect"
)

func marshal(val interface{}) (data []byte, err error) {
v := reflect.ValueOf(val).Elem()
buf := bytes.NewBuffer([]byte{})

err = writeField(v, buf)
data = buf.Bytes()
return
}

func addPadding(data []byte) []byte {
pschou marked this conversation as resolved.
Show resolved Hide resolved
keySize := len(data)
if keySize%aes.BlockSize == 0 {
return data
}
sha := sha1.Sum(data)
padSize := aes.BlockSize - keySize&(aes.BlockSize-1)
data = append(data, make([]byte, padSize)...)
copy(data[keySize:], sha[:])
return data
}

func writeField(v reflect.Value, dst *bytes.Buffer) error {
fieldType := v.Type()

switch fieldType.Kind() {
case reflect.Struct:
for i := 0; i < fieldType.NumField(); i++ {
if fieldType.Field(i).PkgPath != "" {
return fmt.Errorf("struct contains unexported fields")
}

err := writeField(v.Field(i), dst)
if err != nil {
return err
}
}
return nil
}

switch fieldType {
case reflect.TypeOf(string("")):
err := writeString2(dst, v.String())
if err != nil {
return err
}
case reflect.TypeOf([]byte(nil)):
err := writeBytes2(dst, v.Bytes())
if err != nil {
return err
}
case reflect.TypeOf(new(big.Int)):
switch val := (v.Interface()).(type) {
case *big.Int:
err := writeBigInt2(dst, val)
if err != nil {
return err
}
default:
return fmt.Errorf("unable to set big int")
}
default:
return fmt.Errorf("unknown type %s", fieldType)
}

return nil
}

func writeBytes1(dst *bytes.Buffer, data []byte, length uint16) error {
//length := uint16(len(data) * 8)
// write 4 bytes (data size) of the next element size
err := binary.Write(dst, binary.BigEndian, &length)
if err != nil {
return err
}

_, err = dst.Write(data)

return err
}

func writeBigInt1(dst *bytes.Buffer, data *big.Int) error {
b := data.Bytes()

return writeBytes1(dst, b, uint16(data.BitLen()))
}

func writeInt32(dst *bytes.Buffer, val uint32) error {
return binary.Write(dst, binary.BigEndian, &val)
}

func writeBytes2(dst *bytes.Buffer, data []byte) error {
length := uint32(len(data))
// write 4 bytes (data size) of the next element size
err := binary.Write(dst, binary.BigEndian, &length)
if err != nil {
return err
}

_, err = dst.Write(data)

return err
}

func writeString2(dst *bytes.Buffer, data string) error {
return writeBytes2(dst, []byte(data))
}

func writeBigInt2(dst *bytes.Buffer, data *big.Int) error {
b := data.Bytes()
for (len(b) > 8 && b[0] >= 128) || len(b) == 2 || len(b) == 3 {
b = append([]byte{0}, b...)
}
return writeBytes2(dst, b)
}
Loading