Created
May 6, 2019 20:47
-
-
Save eggsbenjamin/fd59285d0b3c48da70d1275ef5eacbf1 to your computer and use it in GitHub Desktop.
Generating an RSA key pair using golang
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://asciinema.org/a/xTRKQpLwAIVTa1rENAfHg6uFV