Skip to content
Snippets Groups Projects
Commit 7e5474d0 authored by Loïck Bonniot's avatar Loïck Bonniot
Browse files

[auth] Add cert functions and update privkey.go

parent d0d3098f
No related branches found
No related tags found
1 merge request!3Add crypto library and CI
package auth
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"time"
)
// GetCertificateRequest creates a request to be sent to any authoritative signer, as a PEM-encoded array of bytes.
//
// It can be safely sent via the network.
func GetCertificateRequest(country, organization, unit, mail string, key *rsa.PrivateKey) ([]byte, error) {
template := &x509.CertificateRequest{
Subject: pkix.Name{
Country: []string{country},
Organization: []string{organization},
OrganizationalUnit: []string{unit},
CommonName: mail,
},
}
der, err := x509.CreateCertificateRequest(rand.Reader, template, key)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: der,
}), nil
}
// PEMToCertificateRequest tries to decode a PEM-encoded array of bytes to a certificate request
func PEMToCertificateRequest(data []byte) (*x509.CertificateRequest, error) {
block, _ := pem.Decode(data)
return x509.ParseCertificateRequest(block.Bytes)
}
// GetCertificate builds a certificate from a certificate request and an authoritative certificate (CA), as a PEM-encoded array of bytes.
// This function assumes that the identity of the signee is valid.
//
// The serial has to be unique.
//
// The generated certificate can safely be distributed to unknown actors.
func GetCertificate(days int, serial int64, req *x509.CertificateRequest, parent *x509.Certificate, key *rsa.PrivateKey) ([]byte, error) {
template := &x509.Certificate{
SerialNumber: big.NewInt(serial),
Subject: req.Subject,
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(0, 0, days),
IsCA: false,
}
der, err := x509.CreateCertificate(rand.Reader, template, parent, req.PublicKey, key)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: der,
}), nil
}
// GetSelfSignedCertificate builds a CA certificate from a private key, as a PEM-encoded array of bytes.
//
// The serial has to be unique.
//
// The generated certificate should be distributed to any other actor in the network under this CA.
func GetSelfSignedCertificate(days int, serial int64, country, organization, unit, cn string, key *rsa.PrivateKey) ([]byte, error) {
template := &x509.Certificate{
SerialNumber: big.NewInt(serial),
Subject: pkix.Name{
Country: []string{country},
Organization: []string{organization},
OrganizationalUnit: []string{unit},
CommonName: cn,
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(0, 0, days),
IsCA: true,
}
der, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: der,
}), nil
}
// PEMToCertificate tries to decode a PEM-encoded array of bytes to a certificate
func PEMToCertificate(data []byte) (*x509.Certificate, error) {
block, _ := pem.Decode(data)
return x509.ParseCertificate(block.Bytes)
}
package auth
import (
"crypto/rsa"
"fmt"
"os"
"testing"
)
var pkey *rsa.PrivateKey
var csrFixture = `-----BEGIN CERTIFICATE REQUEST-----
MIIBgjCB7AIBADBDMQswCQYDVQQGEwJGUjEMMAoGA1UECgwDT1JHMRAwDgYDVQQL
DAdPUkdVTklUMRQwEgYDVQQDDAt0ZXN0QGdvLnRsZDCBnzANBgkqhkiG9w0BAQEF
AAOBjQAwgYkCgYEAtbEFS3VyXHAcNzZ49XKgXzv9SaBszbHWAXmuQlgH4dyjL7OX
w6NOpjSrIW2MVN99/boW1CilMpJzyMRkfkYg2u/HQw1KRUqP62Tl9FIbFjO+rITC
JI4fHsMpOh6+oWw62wf9mbKfL+kmFTTTfAWZpcE/R8IM+vJK4R+6DE7qvXMCAwEA
AaAAMA0GCSqGSIb3DQEBCwUAA4GBAJKNtd8IsxkJyWnOoJjckX+djFxoCNgo7JS1
6evVTU3esDRQ0P4T6oqn4D+yGQlRNtO6/Ko1D9Vv8v14hG7ZJ23Xr6PNBCQEB1a4
vzcnqUbk1ftU8qbOoTEEElEEeGu/gaDYjHPt/P9apngZpV3KXVAepAyRRLdXPfKa
Shc+gMEf
-----END CERTIFICATE REQUEST-----
`
var crtFixture = `-----BEGIN CERTIFICATE-----
MIICRDCCAa2gAwIBAgIJAIf+q5v9t+rTMA0GCSqGSIb3DQEBCwUAMDoxCzAJBgNV
BAYTAkZSMQwwCgYDVQQKDANPUkcxEDAOBgNVBAsMB09SR1VOSVQxCzAJBgNVBAMM
AkNBMCAXDTE1MTEyMDE1NDMwOVoYDzQ3NTMxMDE2MTU0MzA5WjA6MQswCQYDVQQG
EwJGUjEMMAoGA1UECgwDT1JHMRAwDgYDVQQLDAdPUkdVTklUMQswCQYDVQQDDAJD
QTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqNbQWl2UZiOmJcZDA5x2H2U5
m2qC0D3NNPv0jNOm6shGTKhcLH1W8DrDtv5NjRWN1XTpfy0VkrsoyPpsU6PFFzZC
GmkCoXKBD/dvDNrid2MgbURzx+0a+EmUfFh0+tVP2Dzy0zgb/FZWkM6HT0VQ8KAb
SmlRBctiujDV1RBUOm8CAwEAAaNQME4wHQYDVR0OBBYEFNSIxFzdlyGUGBnqsKpd
bS4te57xMB8GA1UdIwQYMBaAFNSIxFzdlyGUGBnqsKpdbS4te57xMAwGA1UdEwQF
MAMBAf8wDQYJKoZIhvcNAQELBQADgYEAECSnGMqnlgyBdqTC02/Uo1jiPqJjLZV1
TRJFHxs4JPAsff+rdAQ1TQVfaNnvAkAoXVzM34xGPkJserMUBc7aQ61WrByGImai
RqEe6wUqHGuH2blNt+2LSSuFWuR02+LxsJARDVSLViAS3lNgXlgGnzOaRs31iwwU
czHnSiYoCog=
-----END CERTIFICATE-----
`
var keyFixture = `-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCo1tBaXZRmI6YlxkMDnHYfZTmbaoLQPc00+/SM06bqyEZMqFws
fVbwOsO2/k2NFY3VdOl/LRWSuyjI+mxTo8UXNkIaaQKhcoEP928M2uJ3YyBtRHPH
7Rr4SZR8WHT61U/YPPLTOBv8VlaQzodPRVDwoBtKaVEFy2K6MNXVEFQ6bwIDAQAB
AoGAWaKZwK/XvhYE+h70qvEgwPAzkjAMvNNio1Nz9GPVROYIdGAZd0Efq6/3Aaqm
r1UXFJDZ+buMrXaRY4mXgxv54MjkX4d4KRVfAfIRbZQlP1jjnT6eRFhLFGOZe6pT
FUxMWO8wJDPwuNJgHFZOm+Ja3v2Hgvt6wgqu+l1onx4RNtECQQDZZbPPtgqTIsrv
BO0PZvL2BLFpg55NUYvpm57JU0/wU+rqG27gVUx7IYGWQ7J61BRIwmMR9HwrDRtE
EwPDelXnAkEAxtHJqHdElDcJGH0Um1WVjd2U229Imo9FBTbvFjj8uaJwk1MYhwks
fUcvD6+s0uoKZAdHogQBM7nN5OrtrnjWOQJBAM+Fpf/BZpbNv6oqqaDqRUNTd4eh
fJuSHF0DkK/eN5DSioyvY0gCJN/lPC6UsOtPR42tAaVCHMV73Ws+O3l+bkECQGvT
pRGHtZrIildMpttjvBtXe/7SSMcCQoWEeIBN4cpvraxI2bmKoSVEcOKJ/SnaIk6D
oDbfAyPhdifbvZHtGQkCQQCDH0Jo3JY7TlOripsIWm8hyikOzw9Lfonhbvnaofjt
amR9w6/SM5D0y20NqMVCmJxHWYW9sRIfZOmRjprYbczH
-----END RSA PRIVATE KEY-----
`
func TestMain(m *testing.M) {
pkey, _ = GeneratePrivateKey(1024)
os.Exit(m.Run())
}
func TestGetCertificateRequest(t *testing.T) {
res, err := GetCertificateRequest("FR", "ORG", "ORGUNIT", "test@go.tld", pkey)
if res == nil || err != nil {
t.Fatal(err)
}
if res[0] != '-' {
t.Fatalf("Bad format\n%s", res)
}
}
func TestPEMToCertificateRequest(t *testing.T) {
res, err := PEMToCertificateRequest([]byte(csrFixture))
if res == nil || err != nil {
t.Fatal(err)
}
if res.Subject.Country[0] != "FR" {
t.Fatal("Wrong country: ", res.Subject.Country)
}
if res.Subject.CommonName != "test@go.tld" {
t.Fatal("Wrong CN: ", res.Subject.CommonName)
}
}
func TestGetSelfSignedCertificate(t *testing.T) {
res, err := GetSelfSignedCertificate(10, 20, "FR", "TEST", "TEST UNIT", "My Cn", pkey)
if res == nil || err != nil {
t.Fatal(err)
}
if res[0] != '-' {
t.Fatalf("Bad format\n%s", res)
}
}
func TestPEMToCertificate(t *testing.T) {
res, err := PEMToCertificate([]byte(crtFixture))
if res == nil || err != nil {
t.Fatal(err)
}
if res.Subject.CommonName != "CA" {
t.Fatal("Wrong CN: ", res.Subject.CommonName)
}
if res.Issuer.CommonName != "CA" {
t.Fatal("Wrong issuer: ", res.Issuer.CommonName)
}
}
func TestGetCertificate(t *testing.T) {
req, _ := PEMToCertificateRequest([]byte(csrFixture))
crt, _ := PEMToCertificate([]byte(crtFixture))
key, _ := PEMToPrivateKey([]byte(keyFixture))
res, err := GetCertificate(10, 21, req, crt, key)
if res == nil || err != nil {
t.Fatal(err)
}
if res[0] != '-' {
t.Fatalf("Bad format\n%s", res)
}
}
func ExampleGetCertificate() {
// Load elements from PEM files
certificateRequest, _ := PEMToCertificateRequest([]byte(csrFixture))
signerCertificate, _ := PEMToCertificate([]byte(crtFixture))
signerKey, _ := PEMToPrivateKey([]byte(keyFixture))
// Generate the certificate for 365 days with a serial of 0x10 (16)
cert, err := GetCertificate(365, int64(0x10), certificateRequest, signerCertificate, signerKey)
if cert == nil || err != nil {
fmt.Println(err)
} else {
fmt.Println("OK")
}
// Output:
// OK
}
......@@ -13,13 +13,7 @@ var Cipher = x509.PEMCipherAES256
// GeneratePrivateKey builds a private key of given size from default random
func GeneratePrivateKey(bits int) (*rsa.PrivateKey, error) {
key, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, err
}
return key, nil
return rsa.GenerateKey(rand.Reader, bits)
}
// PrivateKeyToEncryptedPEM builds a PEM-encoded array of bytes from a private key and a password.
......@@ -66,13 +60,8 @@ func EncryptedPEMToPrivateKey(data []byte, pwd string) (*rsa.PrivateKey, error)
}
}
key, err := x509.ParsePKCS1PrivateKey(decodedData)
if err != nil {
return nil, err
}
return x509.ParsePKCS1PrivateKey(decodedData)
return key, nil
}
// PEMToPrivateKey tries to decode a plain PEM-encoded array of bytes to a private key.
......
......@@ -11,7 +11,7 @@ func TestGeneratePrivateKey(t *testing.T) {
_, err := GeneratePrivateKey(1024)
if err != nil {
t.Fail()
t.Fatal(err)
}
if !testing.Short() {
......@@ -20,52 +20,60 @@ func TestGeneratePrivateKey(t *testing.T) {
}
func TestPrivateKeyToPEM(t *testing.T) {
key, _ := GeneratePrivateKey(2048)
key, _ := GeneratePrivateKey(512)
res := PrivateKeyToPEM(key)
if res[0] != '-' {
t.Fatalf("Bad format\n%s", res)
}
if IsPEMEncrypted(res) {
t.Fail()
t.Fatal("Result is encrypted")
}
}
func TestPrivateKeyToEncryptedPEM(t *testing.T) {
key, _ := GeneratePrivateKey(2048)
key, _ := GeneratePrivateKey(512)
res, err := PrivateKeyToEncryptedPEM(key, "password")
if res[0] != '-' {
t.Fatalf("Bad format\n%s", res)
}
if !IsPEMEncrypted(res) || err != nil {
t.Fail()
t.Fatal("Result is not encrypted: ", err)
}
}
func TestPEMToPrivateKey(t *testing.T) {
key, _ := GeneratePrivateKey(2048)
key, _ := GeneratePrivateKey(512)
key2, err := PEMToPrivateKey(PrivateKeyToPEM(key))
if !reflect.DeepEqual(key, key2) || err != nil {
t.Fail()
t.Fatal(err)
}
}
func TestEncryptedPEMToPrivateKey(t *testing.T) {
key, _ := GeneratePrivateKey(2048)
key, _ := GeneratePrivateKey(512)
res, _ := PrivateKeyToEncryptedPEM(key, "password")
goodKey, err := EncryptedPEMToPrivateKey(res, "password")
if !reflect.DeepEqual(key, goodKey) || err != nil {
t.Fail()
t.Fatal(err)
}
badKey, err := EncryptedPEMToPrivateKey(res, "badpass")
if badKey != nil || err != x509.IncorrectPasswordError {
t.Fail()
t.Fatal(err)
}
}
func ExampleEncryptedPEMToPrivateKey() {
// Generate a new private key for example
key, err := GeneratePrivateKey(2048)
key, err := GeneratePrivateKey(512)
if err != nil {
panic(err)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment