Skip to content

Instantly share code, notes, and snippets.

@goliatone
Forked from azakordonets/generate_rsa_ssh_keys.go
Last active October 16, 2023 07:34
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save goliatone/e9c13e5f046e34cef6e150d06f20a34c to your computer and use it in GitHub Desktop.
Save goliatone/e9c13e5f046e34cef6e150d06f20a34c to your computer and use it in GitHub Desktop.
Generate SSH RSA Private/Public Key pair with Golang
// This shows an example of how to generate a SSH RSA Private/Public key pair and save it locally
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"golang.org/x/crypto/ssh"
"io/ioutil"
"log"
)
func main() {
savePrivateFileTo := "./id_rsa_test"
savePublicFileTo := "./id_rsa_test.pub"
bitSize := 4096
privateKey, err := generatePrivateKey(bitSize)
if err != nil {
log.Fatal(err.Error())
}
publicKeyBytes, err := generatePublicKey(&privateKey.PublicKey)
if err != nil {
log.Fatal(err.Error())
}
privateKeyBytes := encodePrivateKeyToPEM(privateKey)
err = writeKeyToFile(privateKeyBytes, savePrivateFileTo)
if err != nil {
log.Fatal(err.Error())
}
err = writeKeyToFile([]byte(publicKeyBytes), savePublicFileTo)
if err != nil {
log.Fatal(err.Error())
}
}
// generatePrivateKey creates a RSA Private Key of specified byte size
func generatePrivateKey(bitSize int) (*rsa.PrivateKey, error) {
// Private Key generation
privateKey, err := rsa.GenerateKey(rand.Reader, bitSize)
if err != nil {
return nil, err
}
// Validate Private Key
err = privateKey.Validate()
if err != nil {
return nil, err
}
log.Println("Private Key generated")
return privateKey, nil
}
// encodePrivateKeyToPEM encodes Private Key from RSA to PEM format
func encodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte {
// Get ASN.1 DER format
privDER := x509.MarshalPKCS1PrivateKey(privateKey)
// pem.Block
privBlock := pem.Block{
Type: "RSA PRIVATE KEY",
Headers: nil,
Bytes: privDER,
}
// Private key in PEM format
privatePEM := pem.EncodeToMemory(&privBlock)
return privatePEM
}
// generatePublicKey take a rsa.PublicKey and return bytes suitable for writing to .pub file
// returns in the format "ssh-rsa ..."
func generatePublicKey(privatekey *rsa.PublicKey) ([]byte, error) {
publicRsaKey, err := ssh.NewPublicKey(privatekey)
if err != nil {
return nil, err
}
pubKeyBytes := ssh.MarshalAuthorizedKey(publicRsaKey)
log.Println("Public key generated")
return pubKeyBytes, nil
}
// writePemToFile writes keys to a file
func writeKeyToFile(keyBytes []byte, saveFileTo string) error {
err := ioutil.WriteFile(saveFileTo, keyBytes, 0600)
if err != nil {
return err
}
log.Printf("Key saved to: %s", saveFileTo)
return nil
}
import (
"fmt"
"crypto/rsa"
"crypto/rand"
"crypto/x509"
"encoding/pem"
)
// MakeSSHKeyPair make a pair of public and private keys for SSH access.
// Public key is encoded in the format for inclusion in an OpenSSH authorized_keys file.
// Private Key generated is PEM encoded
func MakeSSHKeyPair(pubKeyPath, privateKeyPath string) error {
privateKey, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
return err
}
// generate and write private key as PEM
privateKeyFile, err := os.Create(privateKeyPath)
defer privateKeyFile.Close()
if err != nil {
return err
}
privateKeyPEM := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}
if err := pem.Encode(privateKeyFile, privateKeyPEM); err != nil {
return err
}
// generate and write public key
pub, err := ssh.NewPublicKey(&privateKey.PublicKey)
if err != nil {
return err
}
return ioutil.WriteFile(pubKeyPath, ssh.MarshalAuthorizedKey(pub), 0655)
}
// Released under CC0 1.0 https://creativecommons.org/publicdomain/zero/1.0/
// To the extent possible under law, the author have dedicated all copyright
// and related and neighboring rights to this software to the public domain
// worldwide. This software is distributed without any warranty.
//https://play.golang.org/p/BK9rxDD87ur
//PBES2 format decoder
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/des"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"errors"
"fmt"
"hash"
"os"
"strconv"
"golang.org/x/crypto/pbkdf2"
)
func appendOID(b asn1.ObjectIdentifier, v ...int) asn1.ObjectIdentifier {
n := make(asn1.ObjectIdentifier, len(b), len(b)+len(v))
copy(n, b)
return append(n, v...)
}
var (
oidRSADSI = asn1.ObjectIdentifier{1, 2, 840, 113549}
oidPKCS5 = appendOID(oidRSADSI, 1, 5)
oidPBKDF2 = appendOID(oidPKCS5, 12)
oidPBES2 = appendOID(oidPKCS5, 13)
oidDigestAlgorithm = appendOID(oidRSADSI, 2)
oidHMACWithSHA1 = appendOID(oidDigestAlgorithm, 7)
oidHMACWithSHA224 = appendOID(oidDigestAlgorithm, 8)
oidHMACWithSHA256 = appendOID(oidDigestAlgorithm, 9)
oidHMACWithSHA384 = appendOID(oidDigestAlgorithm, 10)
oidHMACWithSHA512 = appendOID(oidDigestAlgorithm, 11)
oidHMACWithSHA512_224 = appendOID(oidDigestAlgorithm, 12)
oidHMACWithSHA512_256 = appendOID(oidDigestAlgorithm, 13)
oidEncryptionAlgorithm = appendOID(oidRSADSI, 3)
oidDESCBC = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7}
oidDESEDE3CBC = appendOID(oidEncryptionAlgorithm, 7)
oidAES = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1}
oidAES128CBCPAD = appendOID(oidAES, 2)
oidAES192CBCPAD = appendOID(oidAES, 22)
oidAES256CBCPAD = appendOID(oidAES, 42)
)
var (
ErrIncorrectPassword = errors.New("possilbly incorrect encryption password")
errInvalidDataLen = errors.New("data size is not a multiple of cipher block size")
errInvalidIVLen = errors.New("invalid IV size")
errInvalidIter = errors.New("invalid iteration count")
errNoData = errors.New("no data to decrypt")
errNotPBES2 = errors.New("not PBES2 data")
errUnsupportedKDF = errors.New("unsupported KDF")
errUnsupportedPRF = errors.New("unsupported PRF")
errUnsupportedEncS = errors.New("unsupported encryption scheme")
)
type ErrTooManyIterations int
func (err ErrTooManyIterations) Error() string {
return "too many PBKDF2 iterations: " + strconv.Itoa(int(err))
}
func prfByOID(oid asn1.ObjectIdentifier) func() hash.Hash {
if len(oid) == 0 {
return sha1.New
}
if oid.Equal(oidHMACWithSHA1) {
return sha1.New
}
if oid.Equal(oidHMACWithSHA224) {
return sha256.New224
}
if oid.Equal(oidHMACWithSHA256) {
return sha256.New
}
if oid.Equal(oidHMACWithSHA384) {
return sha512.New384
}
if oid.Equal(oidHMACWithSHA512) {
return sha512.New
}
if oid.Equal(oidHMACWithSHA512_224) {
return sha512.New512_224
}
if oid.Equal(oidHMACWithSHA512_256) {
return sha512.New512_256
}
return nil
}
func encsByOID(oid asn1.ObjectIdentifier) (func([]byte) (cipher.Block, error), func(cipher.Block, []byte) cipher.BlockMode, int) {
if oid.Equal(oidDESCBC) {
return des.NewCipher, cipher.NewCBCDecrypter, 8
}
if oid.Equal(oidDESEDE3CBC) {
return des.NewTripleDESCipher, cipher.NewCBCDecrypter, 24
}
if oid.Equal(oidAES128CBCPAD) {
return aes.NewCipher, cipher.NewCBCDecrypter, 16
}
if oid.Equal(oidAES192CBCPAD) {
return aes.NewCipher, cipher.NewCBCDecrypter, 24
}
if oid.Equal(oidAES256CBCPAD) {
return aes.NewCipher, cipher.NewCBCDecrypter, 32
}
return nil, nil, 0
}
func DecryptPBES2(b, password []byte, maxIter int) (data, rest []byte, err error) {
var p struct {
ES struct {
ID asn1.ObjectIdentifier
Params struct {
KDF struct {
ID asn1.ObjectIdentifier
Params struct {
Salt []byte
Iter int
KeyLength int `asn1:"optional"`
PRF struct {
ID asn1.ObjectIdentifier
Params asn1.RawValue
} `asn1:"optional"`
}
}
EncS struct {
ID asn1.ObjectIdentifier
Params []byte
}
}
}
Data []byte
}
rest, err = asn1.Unmarshal(b, &p)
if err != nil {
return
}
if !p.ES.ID.Equal(oidPBES2) {
err = errNotPBES2
return
}
if !p.ES.Params.KDF.ID.Equal(oidPBKDF2) {
err = errUnsupportedKDF
return
}
if p.ES.Params.KDF.Params.Iter < 1 {
err = errInvalidIter
return
}
prf := prfByOID(p.ES.Params.KDF.Params.PRF.ID)
if prf == nil {
err = errUnsupportedPRF
return
}
bcf, bmf, kl := encsByOID(p.ES.Params.EncS.ID)
if bcf == nil || bmf == nil {
err = errUnsupportedEncS
return
}
if len(p.Data) == 0 {
err = errNoData
return
}
if maxIter > 0 && p.ES.Params.KDF.Params.Iter > maxIter {
err = ErrTooManyIterations(p.ES.Params.KDF.Params.Iter)
return
}
key := pbkdf2.Key(password, p.ES.Params.KDF.Params.Salt, p.ES.Params.KDF.Params.Iter, kl, prf)
var bc cipher.Block
bc, err = bcf(key)
if err != nil {
return
}
if len(p.ES.Params.EncS.Params) != bc.BlockSize() {
err = errInvalidIVLen
return
}
bm := bmf(bc, p.ES.Params.EncS.Params)
if len(p.Data)%bm.BlockSize() != 0 {
err = errInvalidDataLen
return
}
data = make([]byte, len(p.Data))
bm.CryptBlocks(data, p.Data)
pl := data[len(data)-1]
if pl == 0 || int(pl) > bm.BlockSize() {
err = ErrIncorrectPassword
return
}
dl := len(data) - int(pl)
for _, b := range data[dl:] {
if b != pl {
err = ErrIncorrectPassword
return
}
}
data = data[:dl]
return
}
var pemKey = ([]byte)(`-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIBSzBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIQAY0IsXMhucCAggA
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECHi4EIJK+T6FBIH4Fatl16Lwznm/
jIhKygStjhIlpww0A0aZDp/D0eEJpXzvPgRZWf2xhlf5gzTMblQ2XkNrbu/OWOOS
f+qx//lh30WTFYOwu0ZWBuxGnjDQav2nc+GKRfzCWbTvgdj8EOKi3vgt8PkuBZWp
IwX0GRrLLd19EmC/VpZ6zAoJIxeE2Oc76tBREJCs5T8o+4Y28rgo/mXbMJmxpdAK
ncWa4y0f1IEcjdw2u3I8csvtwUIj6WjVLkrS1R3I0DS9jEbs0rZ9uORk5aFatzre
ccfQA0JI0n15QPX8dGh/RnWmpzpGXMxShiwn434KGD/Fa0mZeQex26chknoV3YE=
-----END ENCRYPTED PRIVATE KEY-----`)
var password = ([]byte)("1234")
func main() {
block, _ := pem.Decode(pemKey)
if block == nil {
fmt.Fprintln(os.Stderr, "failed to decode PEM block")
os.Exit(1)
}
var derKey []byte
var err error
if block.Type == "ENCRYPTED PRIVATE KEY" {
derKey, _, err = DecryptPBES2(block.Bytes, password, 1000000)
} else if x509.IsEncryptedPEMBlock(block) {
derKey, err = x509.DecryptPEMBlock(block, password)
} else {
derKey = block.Bytes
}
if err != nil {
fmt.Fprintln(os.Stderr, "failed to decrypt private key:", err)
os.Exit(1)
}
key, err := x509.ParsePKCS8PrivateKey(derKey)
if err != nil {
fmt.Fprintln(os.Stderr, "failed to parse PKCS #8 private key:", err)
os.Exit(1)
}
fmt.Printf("key type: %T\n", key)
}
@musamiyas
Copy link

Good evening
Please do you have some way to generate oly the ECDSA public keys from the private keys in sequence using this library "github.com/ethereum/go-ethereum/crypto/secp256k1"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment