100 lines
2.7 KiB
Go
100 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"log"
|
|
"math/big"
|
|
"os"
|
|
)
|
|
|
|
const (
|
|
// bits is the size of the resulting RSA key, also known as "nlen" or "Length
|
|
// of the modulus N". Usually 1024, 2048, or 4096.
|
|
bits = 2048
|
|
// gap is the exponent of the different between the prime factors of the RSA
|
|
// key, i.e. |p-q| ~= 2^gap. For FIPS compliance, set this to (bits/2 - 100).
|
|
gap = 516
|
|
)
|
|
|
|
func main() {
|
|
// Generate q, which will be the smaller of the two factors. We set its length
|
|
// so that the product of two similarly-sized factors will be the desired
|
|
// bit length.
|
|
q, err := rand.Prime(rand.Reader, (bits+1)/2)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
// Our starting point for p is q + 2^gap.
|
|
p := new(big.Int).Add(q, new(big.Int).Exp(big.NewInt(2), big.NewInt(gap), nil))
|
|
|
|
// Now we just keep incrementing P until we find a prime. You might think
|
|
// this would take a while, but it won't: there are a lot of primes.
|
|
attempts := 0
|
|
for {
|
|
// Using 34 rounds of Miller-Rabin primality testing is enough for the go
|
|
// stdlib, so it's enough for us.
|
|
if p.ProbablyPrime(34) {
|
|
break
|
|
}
|
|
|
|
// We know P is odd because it started as a prime (odd) plus a power of two
|
|
// (even), so we can increment by 2 to remain odd.
|
|
p.Add(p, big.NewInt(2))
|
|
attempts++
|
|
}
|
|
|
|
fmt.Println("p:", p.String())
|
|
fmt.Println("q:", q.String())
|
|
fmt.Println("Differ by", fmt.Sprintf("2^%d + %d", gap, 2*attempts))
|
|
|
|
// Construct the public modulus N from the prime factors.
|
|
n := new(big.Int).Mul(p, q)
|
|
|
|
// Construct the public key from the modulus and (fixed) public exponent.
|
|
pubkey := rsa.PublicKey{
|
|
N: n,
|
|
E: 65537,
|
|
}
|
|
|
|
// Construct the private exponent D from the prime factors.
|
|
p_1 := new(big.Int).Sub(p, big.NewInt(1))
|
|
q_1 := new(big.Int).Sub(q, big.NewInt(1))
|
|
field := new(big.Int).Mul(p_1, q_1)
|
|
d := new(big.Int).ModInverse(big.NewInt(65537), field)
|
|
|
|
// Construct the private key from the factors and private exponent.
|
|
privkey := rsa.PrivateKey{
|
|
PublicKey: pubkey,
|
|
D: d,
|
|
Primes: []*big.Int{p, q},
|
|
}
|
|
privkey.Precompute()
|
|
|
|
// Sign a CSR using this key, so we can use it in integration tests.
|
|
// Note that this step *only works on go1.23 and earlier*. Later versions of
|
|
// go detect that the prime factors are too close together and refuse to
|
|
// produce a signature.
|
|
csrDER, err := x509.CreateCertificateRequest(
|
|
rand.Reader,
|
|
&x509.CertificateRequest{
|
|
Subject: pkix.Name{CommonName: "example.com"},
|
|
PublicKey: &pubkey,
|
|
},
|
|
&privkey)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
csrPEM := pem.EncodeToMemory(&pem.Block{
|
|
Type: "CERTIFICATE REQUEST",
|
|
Bytes: csrDER,
|
|
})
|
|
fmt.Fprint(os.Stdout, string(csrPEM))
|
|
}
|