Created
April 15, 2012 17:34
-
-
Save agl/2394040 to your computer and use it in GitHub Desktop.
GPG/X.509 certificates
This file contains hidden or 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 ( | |
"bytes" | |
"code.google.com/p/go.crypto/openpgp" | |
"crypto/rand" | |
"crypto/rsa" | |
"crypto/x509/pkix" | |
"encoding/pem" | |
"fmt" | |
"github.com/agl/gpgx509/x509" | |
"math/big" | |
"os" | |
"time" | |
) | |
// This example code reads a GPG key in "gpg-private-key", generates an RSA | |
// key, signs the RSA key with the GPG key and then creates a self-signed | |
// certificate with the GPG signature embedded. | |
// | |
// The GPG signature is embedded with a OID stolen from the PKIX arc, which | |
// should be replaced with something real before actual use. | |
// | |
// You can export the private key with: | |
// gpg --export-secret-keys "My Identity" > gpg-private-key | |
func main() { | |
gpgKey, err := os.Open("gpg-private-key") | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "Failed to open GPG private key: %s\n", err) | |
return | |
} | |
el, err := openpgp.ReadKeyRing(gpgKey) | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "Failed to parse GPG private key (note: it shouldn't be armored): %s\n", err) | |
return | |
} | |
if len(el) != 1 { | |
fmt.Fprintf(os.Stderr, "Found %d entities in the private key file, only wanted one\n", len(el)) | |
return | |
} | |
rsaPriv, _ := rsa.GenerateKey(rand.Reader, 1024) | |
rsaBytes, err := x509.MarshalPKIXPublicKey(&rsaPriv.PublicKey) | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "Failed to serialise public key: %s\n", err) | |
return | |
} | |
pem.Encode(os.Stdout, &pem.Block{Bytes: rsaBytes, Type: "PUBLIC KEY"}) | |
var message bytes.Buffer | |
message.Write([]byte("GPG/X.509 key signature:")) | |
message.Write(rsaBytes) | |
entity := el[0] | |
var sigBuf bytes.Buffer | |
if err := openpgp.DetachSign(&sigBuf, entity, &message, nil); err != nil { | |
fmt.Fprintf(os.Stderr, "Failed to sign key: %s\n", err) | |
return | |
} | |
maxSerial := new(big.Int).SetInt64(1) | |
maxSerial.Lsh(maxSerial, 128) | |
serial, _ := rand.Int(rand.Reader, maxSerial) | |
now := time.Now() | |
// This picks a *random* identity from the key | |
var identity string | |
for k := range entity.Identities { | |
identity = k | |
} | |
template := x509.Certificate{ | |
SerialNumber: serial, | |
Subject: pkix.Name{ | |
CommonName: "GPG/X.509", | |
}, | |
NotBefore: now.Add(-5 * time.Minute).UTC(), | |
NotAfter: now.AddDate(1, 0, 0).UTC(), // valid for 1 year. | |
KeyUsage: x509.KeyUsageDigitalSignature, | |
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, | |
GPGIdentity: identity, | |
GPGSignature: sigBuf.Bytes(), | |
} | |
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &rsaPriv.PublicKey, rsaPriv) | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "Failed to create certificate: %s\n", err) | |
return | |
} | |
pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) | |
} |
This file contains hidden or 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
// Copyright 2011 The Go Authors. All rights reserved. | |
// Use of this source code is governed by a BSD-style | |
// license that can be found in the LICENSE file. | |
package x509 | |
import ( | |
"crypto/rsa" | |
"encoding/asn1" | |
"errors" | |
"math/big" | |
) | |
// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key. | |
type pkcs1PrivateKey struct { | |
Version int | |
N *big.Int | |
E int | |
D *big.Int | |
P *big.Int | |
Q *big.Int | |
// We ignore these values, if present, because rsa will calculate them. | |
Dp *big.Int `asn1:"optional"` | |
Dq *big.Int `asn1:"optional"` | |
Qinv *big.Int `asn1:"optional"` | |
AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional,omitempty"` | |
} | |
type pkcs1AdditionalRSAPrime struct { | |
Prime *big.Int | |
// We ignore these values because rsa will calculate them. | |
Exp *big.Int | |
Coeff *big.Int | |
} | |
// ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form. | |
func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error) { | |
var priv pkcs1PrivateKey | |
rest, err := asn1.Unmarshal(der, &priv) | |
if len(rest) > 0 { | |
err = asn1.SyntaxError{Msg: "trailing data"} | |
return | |
} | |
if err != nil { | |
return | |
} | |
if priv.Version > 1 { | |
return nil, errors.New("x509: unsupported private key version") | |
} | |
if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 { | |
return nil, errors.New("private key contains zero or negative value") | |
} | |
key = new(rsa.PrivateKey) | |
key.PublicKey = rsa.PublicKey{ | |
E: priv.E, | |
N: priv.N, | |
} | |
key.D = priv.D | |
key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes)) | |
key.Primes[0] = priv.P | |
key.Primes[1] = priv.Q | |
for i, a := range priv.AdditionalPrimes { | |
if a.Prime.Sign() <= 0 { | |
return nil, errors.New("private key contains zero or negative prime") | |
} | |
key.Primes[i+2] = a.Prime | |
// We ignore the other two values because rsa will calculate | |
// them as needed. | |
} | |
err = key.Validate() | |
if err != nil { | |
return nil, err | |
} | |
key.Precompute() | |
return | |
} | |
// MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form. | |
func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte { | |
key.Precompute() | |
version := 0 | |
if len(key.Primes) > 2 { | |
version = 1 | |
} | |
priv := pkcs1PrivateKey{ | |
Version: version, | |
N: key.N, | |
E: key.PublicKey.E, | |
D: key.D, | |
P: key.Primes[0], | |
Q: key.Primes[1], | |
Dp: key.Precomputed.Dp, | |
Dq: key.Precomputed.Dq, | |
Qinv: key.Precomputed.Qinv, | |
} | |
priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues)) | |
for i, values := range key.Precomputed.CRTValues { | |
priv.AdditionalPrimes[i].Prime = key.Primes[2+i] | |
priv.AdditionalPrimes[i].Exp = values.Exp | |
priv.AdditionalPrimes[i].Coeff = values.Coeff | |
} | |
b, _ := asn1.Marshal(priv) | |
return b | |
} | |
// rsaPublicKey reflects the ASN.1 structure of a PKCS#1 public key. | |
type rsaPublicKey struct { | |
N *big.Int | |
E int | |
} |
This file contains hidden or 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
// Copyright 2009 The Go Authors. All rights reserved. | |
// Use of this source code is governed by a BSD-style | |
// license that can be found in the LICENSE file. | |
// Package x509 parses X.509-encoded keys and certificates. | |
package x509 | |
import ( | |
"bytes" | |
"crypto" | |
"crypto/dsa" | |
"crypto/rsa" | |
"crypto/sha1" | |
"crypto/x509/pkix" | |
"encoding/asn1" | |
"encoding/pem" | |
"errors" | |
"io" | |
"math/big" | |
"time" | |
) | |
// pkixPublicKey reflects a PKIX public key structure. See SubjectPublicKeyInfo | |
// in RFC 3280. | |
type pkixPublicKey struct { | |
Algo pkix.AlgorithmIdentifier | |
BitString asn1.BitString | |
} | |
// ParsePKIXPublicKey parses a DER encoded public key. These values are | |
// typically found in PEM blocks with "BEGIN PUBLIC KEY". | |
func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error) { | |
var pki publicKeyInfo | |
if _, err = asn1.Unmarshal(derBytes, &pki); err != nil { | |
return | |
} | |
algo := getPublicKeyAlgorithmFromOID(pki.Algorithm.Algorithm) | |
if algo == UnknownPublicKeyAlgorithm { | |
return nil, errors.New("ParsePKIXPublicKey: unknown public key algorithm") | |
} | |
return parsePublicKey(algo, &pki) | |
} | |
// MarshalPKIXPublicKey serialises a public key to DER-encoded PKIX format. | |
func MarshalPKIXPublicKey(pub interface{}) ([]byte, error) { | |
var pubBytes []byte | |
switch pub := pub.(type) { | |
case *rsa.PublicKey: | |
pubBytes, _ = asn1.Marshal(rsaPublicKey{ | |
N: pub.N, | |
E: pub.E, | |
}) | |
default: | |
return nil, errors.New("MarshalPKIXPublicKey: unknown public key type") | |
} | |
pkix := pkixPublicKey{ | |
Algo: pkix.AlgorithmIdentifier{ | |
Algorithm: []int{1, 2, 840, 113549, 1, 1, 1}, | |
// This is a NULL parameters value which is technically | |
// superfluous, but most other code includes it and, by | |
// doing this, we match their public key hashes. | |
Parameters: asn1.RawValue{ | |
Tag: 5, | |
}, | |
}, | |
BitString: asn1.BitString{ | |
Bytes: pubBytes, | |
BitLength: 8 * len(pubBytes), | |
}, | |
} | |
ret, _ := asn1.Marshal(pkix) | |
return ret, nil | |
} | |
// These structures reflect the ASN.1 structure of X.509 certificates.: | |
type certificate struct { | |
Raw asn1.RawContent | |
TBSCertificate tbsCertificate | |
SignatureAlgorithm pkix.AlgorithmIdentifier | |
SignatureValue asn1.BitString | |
} | |
type tbsCertificate struct { | |
Raw asn1.RawContent | |
Version int `asn1:"optional,explicit,default:1,tag:0"` | |
SerialNumber *big.Int | |
SignatureAlgorithm pkix.AlgorithmIdentifier | |
Issuer asn1.RawValue | |
Validity validity | |
Subject asn1.RawValue | |
PublicKey publicKeyInfo | |
UniqueId asn1.BitString `asn1:"optional,tag:1"` | |
SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"` | |
Extensions []pkix.Extension `asn1:"optional,explicit,tag:3"` | |
} | |
type dsaAlgorithmParameters struct { | |
P, Q, G *big.Int | |
} | |
type dsaSignature struct { | |
R, S *big.Int | |
} | |
type validity struct { | |
NotBefore, NotAfter time.Time | |
} | |
type publicKeyInfo struct { | |
Raw asn1.RawContent | |
Algorithm pkix.AlgorithmIdentifier | |
PublicKey asn1.BitString | |
} | |
// RFC 5280, 4.2.1.1 | |
type authKeyId struct { | |
Id []byte `asn1:"optional,tag:0"` | |
} | |
type SignatureAlgorithm int | |
const ( | |
UnknownSignatureAlgorithm SignatureAlgorithm = iota | |
MD2WithRSA | |
MD5WithRSA | |
SHA1WithRSA | |
SHA256WithRSA | |
SHA384WithRSA | |
SHA512WithRSA | |
DSAWithSHA1 | |
DSAWithSHA256 | |
) | |
type PublicKeyAlgorithm int | |
const ( | |
UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota | |
RSA | |
DSA | |
) | |
// OIDs for signature algorithms | |
// | |
// pkcs-1 OBJECT IDENTIFIER ::= { | |
// iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } | |
// | |
// | |
// RFC 3279 2.2.1 RSA Signature Algorithms | |
// | |
// md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } | |
// | |
// md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } | |
// | |
// sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } | |
// | |
// dsaWithSha1 OBJECT IDENTIFIER ::= { | |
// iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 } | |
// | |
// | |
// RFC 4055 5 PKCS #1 Version 1.5 | |
// | |
// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } | |
// | |
// sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } | |
// | |
// sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } | |
// | |
// | |
// RFC 5758 3.1 DSA Signature Algorithms | |
// | |
// dsaWithSha256 OBJECT IDENTIFIER ::= { | |
// joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101) | |
// csor(3) algorithms(4) id-dsa-with-sha2(3) 2} | |
// | |
var ( | |
oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} | |
oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} | |
oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} | |
oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} | |
oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} | |
oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} | |
oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} | |
oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2} | |
) | |
func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm { | |
switch { | |
case oid.Equal(oidSignatureMD2WithRSA): | |
return MD2WithRSA | |
case oid.Equal(oidSignatureMD5WithRSA): | |
return MD5WithRSA | |
case oid.Equal(oidSignatureSHA1WithRSA): | |
return SHA1WithRSA | |
case oid.Equal(oidSignatureSHA256WithRSA): | |
return SHA256WithRSA | |
case oid.Equal(oidSignatureSHA384WithRSA): | |
return SHA384WithRSA | |
case oid.Equal(oidSignatureSHA512WithRSA): | |
return SHA512WithRSA | |
case oid.Equal(oidSignatureDSAWithSHA1): | |
return DSAWithSHA1 | |
case oid.Equal(oidSignatureDSAWithSHA256): | |
return DSAWithSHA256 | |
} | |
return UnknownSignatureAlgorithm | |
} | |
// RFC 3279, 2.3 Public Key Algorithms | |
// | |
// pkcs-1 OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) | |
// rsadsi(113549) pkcs(1) 1 } | |
// | |
// rsaEncryption OBJECT IDENTIFIER ::== { pkcs1-1 1 } | |
// | |
// id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) | |
// x9-57(10040) x9cm(4) 1 } | |
var ( | |
oidPublicKeyRsa = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} | |
oidPublicKeyDsa = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} | |
) | |
func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm { | |
switch { | |
case oid.Equal(oidPublicKeyRsa): | |
return RSA | |
case oid.Equal(oidPublicKeyDsa): | |
return DSA | |
} | |
return UnknownPublicKeyAlgorithm | |
} | |
// KeyUsage represents the set of actions that are valid for a given key. It's | |
// a bitmap of the KeyUsage* constants. | |
type KeyUsage int | |
const ( | |
KeyUsageDigitalSignature KeyUsage = 1 << iota | |
KeyUsageContentCommitment | |
KeyUsageKeyEncipherment | |
KeyUsageDataEncipherment | |
KeyUsageKeyAgreement | |
KeyUsageCertSign | |
KeyUsageCRLSign | |
KeyUsageEncipherOnly | |
KeyUsageDecipherOnly | |
) | |
// RFC 5280, 4.2.1.12 Extended Key Usage | |
// | |
// anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 } | |
// | |
// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } | |
// | |
// id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } | |
// id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } | |
// id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } | |
// id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } | |
// id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } | |
// id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } | |
var ( | |
oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0} | |
oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1} | |
oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2} | |
oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3} | |
oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4} | |
oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8} | |
oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9} | |
) | |
// ExtKeyUsage represents an extended set of actions that are valid for a given key. | |
// Each of the ExtKeyUsage* constants define a unique action. | |
type ExtKeyUsage int | |
const ( | |
ExtKeyUsageAny ExtKeyUsage = iota | |
ExtKeyUsageServerAuth | |
ExtKeyUsageClientAuth | |
ExtKeyUsageCodeSigning | |
ExtKeyUsageEmailProtection | |
ExtKeyUsageTimeStamping | |
ExtKeyUsageOCSPSigning | |
) | |
// A Certificate represents an X.509 certificate. | |
type Certificate struct { | |
Raw []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature). | |
RawTBSCertificate []byte // Certificate part of raw ASN.1 DER content. | |
RawSubjectPublicKeyInfo []byte // DER encoded SubjectPublicKeyInfo. | |
RawSubject []byte // DER encoded Subject | |
RawIssuer []byte // DER encoded Issuer | |
Signature []byte | |
SignatureAlgorithm SignatureAlgorithm | |
PublicKeyAlgorithm PublicKeyAlgorithm | |
PublicKey interface{} | |
Version int | |
SerialNumber *big.Int | |
Issuer pkix.Name | |
Subject pkix.Name | |
NotBefore, NotAfter time.Time // Validity bounds. | |
KeyUsage KeyUsage | |
ExtKeyUsage []ExtKeyUsage // Sequence of extended key usages. | |
UnknownExtKeyUsage []asn1.ObjectIdentifier // Encountered extended key usages unknown to this package. | |
BasicConstraintsValid bool // if true then the next two fields are valid. | |
IsCA bool | |
MaxPathLen int | |
GPGIdentity string | |
GPGSignature []byte | |
SubjectKeyId []byte | |
AuthorityKeyId []byte | |
// Subject Alternate Name values | |
DNSNames []string | |
EmailAddresses []string | |
// Name constraints | |
PermittedDNSDomainsCritical bool // if true then the name constraints are marked critical. | |
PermittedDNSDomains []string | |
PolicyIdentifiers []asn1.ObjectIdentifier | |
} | |
// ErrUnsupportedAlgorithm results from attempting to perform an operation that | |
// involves algorithms that are not currently implemented. | |
var ErrUnsupportedAlgorithm = errors.New("crypto/x509: cannot verify signature: algorithm unimplemented") | |
// ConstraintViolationError results when a requested usage is not permitted by | |
// a certificate. For example: checking a signature when the public key isn't a | |
// certificate signing key. | |
type ConstraintViolationError struct{} | |
func (ConstraintViolationError) Error() string { | |
return "crypto/x509: invalid signature: parent certificate cannot sign this kind of certificate" | |
} | |
func (c *Certificate) Equal(other *Certificate) bool { | |
return bytes.Equal(c.Raw, other.Raw) | |
} | |
// CheckSignatureFrom verifies that the signature on c is a valid signature | |
// from parent. | |
func (c *Certificate) CheckSignatureFrom(parent *Certificate) (err error) { | |
// RFC 5280, 4.2.1.9: | |
// "If the basic constraints extension is not present in a version 3 | |
// certificate, or the extension is present but the cA boolean is not | |
// asserted, then the certified public key MUST NOT be used to verify | |
// certificate signatures." | |
if parent.Version == 3 && !parent.BasicConstraintsValid || | |
parent.BasicConstraintsValid && !parent.IsCA { | |
return ConstraintViolationError{} | |
} | |
if parent.KeyUsage != 0 && parent.KeyUsage&KeyUsageCertSign == 0 { | |
return ConstraintViolationError{} | |
} | |
if parent.PublicKeyAlgorithm == UnknownPublicKeyAlgorithm { | |
return ErrUnsupportedAlgorithm | |
} | |
// TODO(agl): don't ignore the path length constraint. | |
return parent.CheckSignature(c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature) | |
} | |
// CheckSignature verifies that signature is a valid signature over signed from | |
// c's public key. | |
func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature []byte) (err error) { | |
var hashType crypto.Hash | |
switch algo { | |
case SHA1WithRSA, DSAWithSHA1: | |
hashType = crypto.SHA1 | |
case SHA256WithRSA, DSAWithSHA256: | |
hashType = crypto.SHA256 | |
case SHA384WithRSA: | |
hashType = crypto.SHA384 | |
case SHA512WithRSA: | |
hashType = crypto.SHA512 | |
default: | |
return ErrUnsupportedAlgorithm | |
} | |
h := hashType.New() | |
if h == nil { | |
return ErrUnsupportedAlgorithm | |
} | |
h.Write(signed) | |
digest := h.Sum(nil) | |
switch pub := c.PublicKey.(type) { | |
case *rsa.PublicKey: | |
return rsa.VerifyPKCS1v15(pub, hashType, digest, signature) | |
case *dsa.PublicKey: | |
dsaSig := new(dsaSignature) | |
if _, err := asn1.Unmarshal(signature, dsaSig); err != nil { | |
return err | |
} | |
if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { | |
return errors.New("DSA signature contained zero or negative values") | |
} | |
if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) { | |
return errors.New("DSA verification failure") | |
} | |
return | |
} | |
return ErrUnsupportedAlgorithm | |
} | |
// CheckCRLSignature checks that the signature in crl is from c. | |
func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) (err error) { | |
algo := getSignatureAlgorithmFromOID(crl.SignatureAlgorithm.Algorithm) | |
return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign()) | |
} | |
type UnhandledCriticalExtension struct{} | |
func (h UnhandledCriticalExtension) Error() string { | |
return "unhandled critical extension" | |
} | |
type basicConstraints struct { | |
IsCA bool `asn1:"optional"` | |
MaxPathLen int `asn1:"optional,default:-1"` | |
} | |
// RFC 5280 4.2.1.4 | |
type policyInformation struct { | |
Policy asn1.ObjectIdentifier | |
// policyQualifiers omitted | |
} | |
// RFC 5280, 4.2.1.10 | |
type nameConstraints struct { | |
Permitted []generalSubtree `asn1:"optional,tag:0"` | |
Excluded []generalSubtree `asn1:"optional,tag:1"` | |
} | |
type generalSubtree struct { | |
Name string `asn1:"tag:2,optional,ia5"` | |
Min int `asn1:"optional,tag:0"` | |
Max int `asn1:"optional,tag:1"` | |
} | |
func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, error) { | |
asn1Data := keyData.PublicKey.RightAlign() | |
switch algo { | |
case RSA: | |
p := new(rsaPublicKey) | |
_, err := asn1.Unmarshal(asn1Data, p) | |
if err != nil { | |
return nil, err | |
} | |
pub := &rsa.PublicKey{ | |
E: p.E, | |
N: p.N, | |
} | |
return pub, nil | |
case DSA: | |
var p *big.Int | |
_, err := asn1.Unmarshal(asn1Data, &p) | |
if err != nil { | |
return nil, err | |
} | |
paramsData := keyData.Algorithm.Parameters.FullBytes | |
params := new(dsaAlgorithmParameters) | |
_, err = asn1.Unmarshal(paramsData, params) | |
if err != nil { | |
return nil, err | |
} | |
if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 { | |
return nil, errors.New("zero or negative DSA parameter") | |
} | |
pub := &dsa.PublicKey{ | |
Parameters: dsa.Parameters{ | |
P: params.P, | |
Q: params.Q, | |
G: params.G, | |
}, | |
Y: p, | |
} | |
return pub, nil | |
default: | |
return nil, nil | |
} | |
panic("unreachable") | |
} | |
func parseCertificate(in *certificate) (*Certificate, error) { | |
out := new(Certificate) | |
out.Raw = in.Raw | |
out.RawTBSCertificate = in.TBSCertificate.Raw | |
out.RawSubjectPublicKeyInfo = in.TBSCertificate.PublicKey.Raw | |
out.RawSubject = in.TBSCertificate.Subject.FullBytes | |
out.RawIssuer = in.TBSCertificate.Issuer.FullBytes | |
out.Signature = in.SignatureValue.RightAlign() | |
out.SignatureAlgorithm = | |
getSignatureAlgorithmFromOID(in.TBSCertificate.SignatureAlgorithm.Algorithm) | |
out.PublicKeyAlgorithm = | |
getPublicKeyAlgorithmFromOID(in.TBSCertificate.PublicKey.Algorithm.Algorithm) | |
var err error | |
out.PublicKey, err = parsePublicKey(out.PublicKeyAlgorithm, &in.TBSCertificate.PublicKey) | |
if err != nil { | |
return nil, err | |
} | |
if in.TBSCertificate.SerialNumber.Sign() < 0 { | |
return nil, errors.New("negative serial number") | |
} | |
out.Version = in.TBSCertificate.Version + 1 | |
out.SerialNumber = in.TBSCertificate.SerialNumber | |
var issuer, subject pkix.RDNSequence | |
if _, err := asn1.Unmarshal(in.TBSCertificate.Subject.FullBytes, &subject); err != nil { | |
return nil, err | |
} | |
if _, err := asn1.Unmarshal(in.TBSCertificate.Issuer.FullBytes, &issuer); err != nil { | |
return nil, err | |
} | |
out.Issuer.FillFromRDNSequence(&issuer) | |
out.Subject.FillFromRDNSequence(&subject) | |
out.NotBefore = in.TBSCertificate.Validity.NotBefore | |
out.NotAfter = in.TBSCertificate.Validity.NotAfter | |
for _, e := range in.TBSCertificate.Extensions { | |
if len(e.Id) == 4 && e.Id[0] == 2 && e.Id[1] == 5 && e.Id[2] == 29 { | |
switch e.Id[3] { | |
case 15: | |
// RFC 5280, 4.2.1.3 | |
var usageBits asn1.BitString | |
_, err := asn1.Unmarshal(e.Value, &usageBits) | |
if err == nil { | |
var usage int | |
for i := 0; i < 9; i++ { | |
if usageBits.At(i) != 0 { | |
usage |= 1 << uint(i) | |
} | |
} | |
out.KeyUsage = KeyUsage(usage) | |
continue | |
} | |
case 19: | |
// RFC 5280, 4.2.1.9 | |
var constraints basicConstraints | |
_, err := asn1.Unmarshal(e.Value, &constraints) | |
if err == nil { | |
out.BasicConstraintsValid = true | |
out.IsCA = constraints.IsCA | |
out.MaxPathLen = constraints.MaxPathLen | |
continue | |
} | |
case 17: | |
// RFC 5280, 4.2.1.6 | |
// SubjectAltName ::= GeneralNames | |
// | |
// GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName | |
// | |
// GeneralName ::= CHOICE { | |
// otherName [0] OtherName, | |
// rfc822Name [1] IA5String, | |
// dNSName [2] IA5String, | |
// x400Address [3] ORAddress, | |
// directoryName [4] Name, | |
// ediPartyName [5] EDIPartyName, | |
// uniformResourceIdentifier [6] IA5String, | |
// iPAddress [7] OCTET STRING, | |
// registeredID [8] OBJECT IDENTIFIER } | |
var seq asn1.RawValue | |
_, err := asn1.Unmarshal(e.Value, &seq) | |
if err != nil { | |
return nil, err | |
} | |
if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 { | |
return nil, asn1.StructuralError{Msg: "bad SAN sequence"} | |
} | |
parsedName := false | |
rest := seq.Bytes | |
for len(rest) > 0 { | |
var v asn1.RawValue | |
rest, err = asn1.Unmarshal(rest, &v) | |
if err != nil { | |
return nil, err | |
} | |
switch v.Tag { | |
case 1: | |
out.EmailAddresses = append(out.EmailAddresses, string(v.Bytes)) | |
parsedName = true | |
case 2: | |
out.DNSNames = append(out.DNSNames, string(v.Bytes)) | |
parsedName = true | |
} | |
} | |
if parsedName { | |
continue | |
} | |
// If we didn't parse any of the names then we | |
// fall through to the critical check below. | |
case 30: | |
// RFC 5280, 4.2.1.10 | |
// NameConstraints ::= SEQUENCE { | |
// permittedSubtrees [0] GeneralSubtrees OPTIONAL, | |
// excludedSubtrees [1] GeneralSubtrees OPTIONAL } | |
// | |
// GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree | |
// | |
// GeneralSubtree ::= SEQUENCE { | |
// base GeneralName, | |
// minimum [0] BaseDistance DEFAULT 0, | |
// maximum [1] BaseDistance OPTIONAL } | |
// | |
// BaseDistance ::= INTEGER (0..MAX) | |
var constraints nameConstraints | |
_, err := asn1.Unmarshal(e.Value, &constraints) | |
if err != nil { | |
return nil, err | |
} | |
if len(constraints.Excluded) > 0 && e.Critical { | |
return out, UnhandledCriticalExtension{} | |
} | |
for _, subtree := range constraints.Permitted { | |
if subtree.Min > 0 || subtree.Max > 0 || len(subtree.Name) == 0 { | |
if e.Critical { | |
return out, UnhandledCriticalExtension{} | |
} | |
continue | |
} | |
out.PermittedDNSDomains = append(out.PermittedDNSDomains, subtree.Name) | |
} | |
continue | |
case 35: | |
// RFC 5280, 4.2.1.1 | |
var a authKeyId | |
_, err = asn1.Unmarshal(e.Value, &a) | |
if err != nil { | |
return nil, err | |
} | |
out.AuthorityKeyId = a.Id | |
continue | |
case 37: | |
// RFC 5280, 4.2.1.12. Extended Key Usage | |
// id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 } | |
// | |
// ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId | |
// | |
// KeyPurposeId ::= OBJECT IDENTIFIER | |
var keyUsage []asn1.ObjectIdentifier | |
_, err = asn1.Unmarshal(e.Value, &keyUsage) | |
if err != nil { | |
return nil, err | |
} | |
for _, u := range keyUsage { | |
switch { | |
case u.Equal(oidExtKeyUsageAny): | |
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageAny) | |
case u.Equal(oidExtKeyUsageServerAuth): | |
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageServerAuth) | |
case u.Equal(oidExtKeyUsageClientAuth): | |
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageClientAuth) | |
case u.Equal(oidExtKeyUsageCodeSigning): | |
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageCodeSigning) | |
case u.Equal(oidExtKeyUsageEmailProtection): | |
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageEmailProtection) | |
case u.Equal(oidExtKeyUsageTimeStamping): | |
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageTimeStamping) | |
case u.Equal(oidExtKeyUsageOCSPSigning): | |
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageOCSPSigning) | |
default: | |
out.UnknownExtKeyUsage = append(out.UnknownExtKeyUsage, u) | |
} | |
} | |
continue | |
case 14: | |
// RFC 5280, 4.2.1.2 | |
var keyid []byte | |
_, err = asn1.Unmarshal(e.Value, &keyid) | |
if err != nil { | |
return nil, err | |
} | |
out.SubjectKeyId = keyid | |
continue | |
case 32: | |
// RFC 5280 4.2.1.4: Certificate Policies | |
var policies []policyInformation | |
if _, err = asn1.Unmarshal(e.Value, &policies); err != nil { | |
return nil, err | |
} | |
out.PolicyIdentifiers = make([]asn1.ObjectIdentifier, len(policies)) | |
for i, policy := range policies { | |
out.PolicyIdentifiers[i] = policy.Policy | |
} | |
} | |
} | |
if e.Critical { | |
return out, UnhandledCriticalExtension{} | |
} | |
} | |
return out, nil | |
} | |
// ParseCertificate parses a single certificate from the given ASN.1 DER data. | |
func ParseCertificate(asn1Data []byte) (*Certificate, error) { | |
var cert certificate | |
rest, err := asn1.Unmarshal(asn1Data, &cert) | |
if err != nil { | |
return nil, err | |
} | |
if len(rest) > 0 { | |
return nil, asn1.SyntaxError{Msg: "trailing data"} | |
} | |
return parseCertificate(&cert) | |
} | |
// ParseCertificates parses one or more certificates from the given ASN.1 DER | |
// data. The certificates must be concatenated with no intermediate padding. | |
func ParseCertificates(asn1Data []byte) ([]*Certificate, error) { | |
var v []*certificate | |
for len(asn1Data) > 0 { | |
cert := new(certificate) | |
var err error | |
asn1Data, err = asn1.Unmarshal(asn1Data, cert) | |
if err != nil { | |
return nil, err | |
} | |
v = append(v, cert) | |
} | |
ret := make([]*Certificate, len(v)) | |
for i, ci := range v { | |
cert, err := parseCertificate(ci) | |
if err != nil { | |
return nil, err | |
} | |
ret[i] = cert | |
} | |
return ret, nil | |
} | |
func reverseBitsInAByte(in byte) byte { | |
b1 := in>>4 | in<<4 | |
b2 := b1>>2&0x33 | b1<<2&0xcc | |
b3 := b2>>1&0x55 | b2<<1&0xaa | |
return b3 | |
} | |
var ( | |
oidExtensionSubjectKeyId = []int{2, 5, 29, 14} | |
oidExtensionKeyUsage = []int{2, 5, 29, 15} | |
oidExtensionAuthorityKeyId = []int{2, 5, 29, 35} | |
oidExtensionBasicConstraints = []int{2, 5, 29, 19} | |
oidExtensionSubjectAltName = []int{2, 5, 29, 17} | |
oidExtensionCertificatePolicies = []int{2, 5, 29, 32} | |
oidExtensionNameConstraints = []int{2, 5, 29, 30} | |
oidGPGSignature = []int{2, 5, 29, 666666} | |
) | |
func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { | |
ret = make([]pkix.Extension, 8 /* maximum number of elements. */) | |
n := 0 | |
if template.KeyUsage != 0 { | |
ret[n].Id = oidExtensionKeyUsage | |
ret[n].Critical = true | |
var a [2]byte | |
a[0] = reverseBitsInAByte(byte(template.KeyUsage)) | |
a[1] = reverseBitsInAByte(byte(template.KeyUsage >> 8)) | |
l := 1 | |
if a[1] != 0 { | |
l = 2 | |
} | |
ret[n].Value, err = asn1.Marshal(asn1.BitString{Bytes: a[0:l], BitLength: l * 8}) | |
if err != nil { | |
return | |
} | |
n++ | |
} | |
if template.BasicConstraintsValid { | |
ret[n].Id = oidExtensionBasicConstraints | |
ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, template.MaxPathLen}) | |
ret[n].Critical = true | |
if err != nil { | |
return | |
} | |
n++ | |
} | |
if len(template.SubjectKeyId) > 0 { | |
ret[n].Id = oidExtensionSubjectKeyId | |
ret[n].Value, err = asn1.Marshal(template.SubjectKeyId) | |
if err != nil { | |
return | |
} | |
n++ | |
} | |
if len(template.AuthorityKeyId) > 0 { | |
ret[n].Id = oidExtensionAuthorityKeyId | |
ret[n].Value, err = asn1.Marshal(authKeyId{template.AuthorityKeyId}) | |
if err != nil { | |
return | |
} | |
n++ | |
} | |
if len(template.DNSNames) > 0 { | |
ret[n].Id = oidExtensionSubjectAltName | |
rawValues := make([]asn1.RawValue, len(template.DNSNames)) | |
for i, name := range template.DNSNames { | |
rawValues[i] = asn1.RawValue{Tag: 2, Class: 2, Bytes: []byte(name)} | |
} | |
ret[n].Value, err = asn1.Marshal(rawValues) | |
if err != nil { | |
return | |
} | |
n++ | |
} | |
if len(template.PolicyIdentifiers) > 0 { | |
ret[n].Id = oidExtensionCertificatePolicies | |
policies := make([]policyInformation, len(template.PolicyIdentifiers)) | |
for i, policy := range template.PolicyIdentifiers { | |
policies[i].Policy = policy | |
} | |
ret[n].Value, err = asn1.Marshal(policies) | |
if err != nil { | |
return | |
} | |
n++ | |
} | |
if len(template.PermittedDNSDomains) > 0 { | |
ret[n].Id = oidExtensionNameConstraints | |
ret[n].Critical = template.PermittedDNSDomainsCritical | |
var out nameConstraints | |
out.Permitted = make([]generalSubtree, len(template.PermittedDNSDomains)) | |
for i, permitted := range template.PermittedDNSDomains { | |
out.Permitted[i] = generalSubtree{Name: permitted} | |
} | |
ret[n].Value, err = asn1.Marshal(out) | |
if err != nil { | |
return | |
} | |
n++ | |
} | |
if len(template.GPGSignature) > 0 { | |
ret[n].Id = oidGPGSignature | |
type gpgBlob struct { | |
Identity string | |
Signature []byte | |
} | |
ret[n].Value, err = asn1.Marshal(gpgBlob{ | |
Identity: template.GPGIdentity, | |
Signature: template.GPGSignature, | |
}) | |
if err != nil { | |
return | |
} | |
n++ | |
} | |
// Adding another extension here? Remember to update the maximum number | |
// of elements in the make() at the top of the function. | |
return ret[0:n], nil | |
} | |
var ( | |
oidSHA1WithRSA = []int{1, 2, 840, 113549, 1, 1, 5} | |
oidRSA = []int{1, 2, 840, 113549, 1, 1, 1} | |
) | |
func subjectBytes(cert *Certificate) ([]byte, error) { | |
if len(cert.RawSubject) > 0 { | |
return cert.RawSubject, nil | |
} | |
return asn1.Marshal(cert.Subject.ToRDNSequence()) | |
} | |
// CreateCertificate creates a new certificate based on a template. The | |
// following members of template are used: SerialNumber, Subject, NotBefore, | |
// NotAfter, KeyUsage, BasicConstraintsValid, IsCA, MaxPathLen, SubjectKeyId, | |
// DNSNames, PermittedDNSDomainsCritical, PermittedDNSDomains. | |
// | |
// The certificate is signed by parent. If parent is equal to template then the | |
// certificate is self-signed. The parameter pub is the public key of the | |
// signee and priv is the private key of the signer. | |
// | |
// The returned slice is the certificate in DER encoding. | |
// | |
// The only supported key type is RSA (*rsa.PublicKey for pub, *rsa.PrivateKey | |
// for priv). | |
func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interface{}, priv interface{}) (cert []byte, err error) { | |
rsaPub, ok := pub.(*rsa.PublicKey) | |
if !ok { | |
return nil, errors.New("x509: non-RSA public keys not supported") | |
} | |
rsaPriv, ok := priv.(*rsa.PrivateKey) | |
if !ok { | |
return nil, errors.New("x509: non-RSA private keys not supported") | |
} | |
asn1PublicKey, err := asn1.Marshal(rsaPublicKey{ | |
N: rsaPub.N, | |
E: rsaPub.E, | |
}) | |
if err != nil { | |
return | |
} | |
if len(parent.SubjectKeyId) > 0 { | |
template.AuthorityKeyId = parent.SubjectKeyId | |
} | |
extensions, err := buildExtensions(template) | |
if err != nil { | |
return | |
} | |
asn1Issuer, err := subjectBytes(parent) | |
if err != nil { | |
return | |
} | |
asn1Subject, err := subjectBytes(template) | |
if err != nil { | |
return | |
} | |
encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey} | |
c := tbsCertificate{ | |
Version: 2, | |
SerialNumber: template.SerialNumber, | |
SignatureAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA}, | |
Issuer: asn1.RawValue{FullBytes: asn1Issuer}, | |
Validity: validity{template.NotBefore, template.NotAfter}, | |
Subject: asn1.RawValue{FullBytes: asn1Subject}, | |
PublicKey: publicKeyInfo{nil, pkix.AlgorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey}, | |
Extensions: extensions, | |
} | |
tbsCertContents, err := asn1.Marshal(c) | |
if err != nil { | |
return | |
} | |
c.Raw = tbsCertContents | |
h := sha1.New() | |
h.Write(tbsCertContents) | |
digest := h.Sum(nil) | |
signature, err := rsa.SignPKCS1v15(rand, rsaPriv, crypto.SHA1, digest) | |
if err != nil { | |
return | |
} | |
cert, err = asn1.Marshal(certificate{ | |
nil, | |
c, | |
pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA}, | |
asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, | |
}) | |
return | |
} | |
// pemCRLPrefix is the magic string that indicates that we have a PEM encoded | |
// CRL. | |
var pemCRLPrefix = []byte("-----BEGIN X509 CRL") | |
// pemType is the type of a PEM encoded CRL. | |
var pemType = "X509 CRL" | |
// ParseCRL parses a CRL from the given bytes. It's often the case that PEM | |
// encoded CRLs will appear where they should be DER encoded, so this function | |
// will transparently handle PEM encoding as long as there isn't any leading | |
// garbage. | |
func ParseCRL(crlBytes []byte) (certList *pkix.CertificateList, err error) { | |
if bytes.HasPrefix(crlBytes, pemCRLPrefix) { | |
block, _ := pem.Decode(crlBytes) | |
if block != nil && block.Type == pemType { | |
crlBytes = block.Bytes | |
} | |
} | |
return ParseDERCRL(crlBytes) | |
} | |
// ParseDERCRL parses a DER encoded CRL from the given bytes. | |
func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err error) { | |
certList = new(pkix.CertificateList) | |
_, err = asn1.Unmarshal(derBytes, certList) | |
if err != nil { | |
certList = nil | |
} | |
return | |
} | |
// CreateCRL returns a DER encoded CRL, signed by this Certificate, that | |
// contains the given list of revoked certificates. | |
// | |
// The only supported key type is RSA (*rsa.PrivateKey for priv). | |
func (c *Certificate) CreateCRL(rand io.Reader, priv interface{}, revokedCerts []pkix.RevokedCertificate, now, expiry time.Time) (crlBytes []byte, err error) { | |
rsaPriv, ok := priv.(*rsa.PrivateKey) | |
if !ok { | |
return nil, errors.New("x509: non-RSA private keys not supported") | |
} | |
tbsCertList := pkix.TBSCertificateList{ | |
Version: 2, | |
Signature: pkix.AlgorithmIdentifier{ | |
Algorithm: oidSignatureSHA1WithRSA, | |
}, | |
Issuer: c.Subject.ToRDNSequence(), | |
ThisUpdate: now, | |
NextUpdate: expiry, | |
RevokedCertificates: revokedCerts, | |
} | |
tbsCertListContents, err := asn1.Marshal(tbsCertList) | |
if err != nil { | |
return | |
} | |
h := sha1.New() | |
h.Write(tbsCertListContents) | |
digest := h.Sum(nil) | |
signature, err := rsa.SignPKCS1v15(rand, rsaPriv, crypto.SHA1, digest) | |
if err != nil { | |
return | |
} | |
return asn1.Marshal(pkix.CertificateList{ | |
TBSCertList: tbsCertList, | |
SignatureAlgorithm: pkix.AlgorithmIdentifier{ | |
Algorithm: oidSignatureSHA1WithRSA, | |
}, | |
SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment