Skip to content

Instantly share code, notes, and snippets.

@eggsbenjamin
Created May 6, 2019 20:47
Show Gist options
  • Save eggsbenjamin/fd59285d0b3c48da70d1275ef5eacbf1 to your computer and use it in GitHub Desktop.
Save eggsbenjamin/fd59285d0b3c48da70d1275ef5eacbf1 to your computer and use it in GitHub Desktop.
Generating an RSA key pair using golang
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"flag"
"log"
"math/big"
"os"
"golang.org/x/crypto/ssh"
)
var bigOne = big.NewInt(1)
func main() {
/*
This program is the product of a weekend learning about security and
IS NOT intended for anything aside from learning and fun :)
See https://simple.wikipedia.org/wiki/RSA_algorithm for algorithm
- choose two large primes p and q
- calculate n = pq
- calculate e where e > 1 and e and (p - 1)(q - 1) are co-prime
- n and e are the halves of the public key
*/
validBitLengths := map[int]struct{}{
2048: struct{}{},
4096: struct{}{},
}
bitSizeFlag := flag.Int("bit-size", 2048, "bit size of keys")
outPathFlag := flag.String("out", "id_rsa", "path of output files (public key has suffix .pub)")
flag.Parse()
var (
bitSize = *bitSizeFlag
privateKeyOutPath = *outPathFlag
publicKeyOutPath = *outPathFlag + ".pub"
)
if _, ok := validBitLengths[bitSize]; !ok {
log.Fatalf("invalid bit size: %d", bitSize)
}
log.Printf("creating rsa key pair with bit size: %d\n", bitSize)
/*
See https://simple.wikipedia.org/wiki/RSA_algorithm for algorithm
- choose two large primes p and q
- calculate n = pq
- calculate e where e > 1 and e and (p - 1)(q - 1) are co-prime
- n and e are the halves of the public key
*/
// create two random primes p and q
p, err := rand.Prime(rand.Reader, bitSize)
if err != nil {
log.Fatalf("error generating prime: %q", err)
}
q, err := rand.Prime(rand.Reader, bitSize)
if err != nil {
log.Fatalf("error generating prime: %q", err)
}
// calculate phi (p - 1)(q - 1)
phi := new(big.Int).Mul(new(big.Int).Sub(p, bigOne), new(big.Int).Sub(q, bigOne))
// calculate n p*q
n := new(big.Int).Mul(p, q)
// generate e by finding a number that is coprime with phi
var e *big.Int
for {
e, err = rand.Prime(rand.Reader, 32)
if err != nil {
log.Fatalf("error generating prime: %q", err)
}
if areCoprime(e, phi) {
break
}
}
// find d (private key component) which is the modular inverse of e and phi
d := new(big.Int).ModInverse(e, phi)
publicKey := rsa.PublicKey{
N: n,
E: int(e.Int64()),
}
privateKey := rsa.PrivateKey{
D: d,
Primes: []*big.Int{p, q},
PublicKey: publicKey,
}
openSSHPublicKey, err := ssh.NewPublicKey(&publicKey)
if err != nil {
log.Fatalf("error creating openssh public key: %q", err)
}
publicKeyFile, err := os.Create(publicKeyOutPath)
if err != nil {
log.Fatalf("error creating public key file: %q", err)
}
defer publicKeyFile.Close()
privateKeyFile, err := os.Create(privateKeyOutPath)
if err != nil {
log.Fatalf("error creating private key file: %q", err)
}
defer privateKeyFile.Close()
// output the public key in the openssh format
log.Printf("writing public key to %s", publicKeyFile.Name())
if _, err := publicKeyFile.Write(ssh.MarshalAuthorizedKey(openSSHPublicKey)); err != nil {
log.Fatalf("error creating public key pem: %q", err)
}
// output the private key in pem format
log.Printf("writing private key to %s", privateKeyFile.Name())
if err := pem.Encode(privateKeyFile, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(&privateKey),
}); err != nil {
log.Fatalf("error creating private key pem: %q", err)
}
}
// areCoprime asserts that the highest common divisor belonging to two integers is one.
func areCoprime(x *big.Int, y *big.Int) bool {
return x.Cmp(bigOne) == 1 && x.Cmp(y) < 0 && new(big.Int).GCD(nil, nil, x, y).Cmp(bigOne) == 0
}
@eggsbenjamin
Copy link
Author

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