func addChain(logClient *client.LogClient) { if *certChain == "" { log.Fatalf("No certificate chain file specified with -cert_chain") } rest, err := ioutil.ReadFile(*certChain) if err != nil { log.Fatalf("Failed to read certificate file: %v", err) } var chain []ct.ASN1Cert for { var block *pem.Block block, rest = pem.Decode(rest) if block == nil { break } if block.Type == "CERTIFICATE" { chain = append(chain, ct.ASN1Cert(block.Bytes)) } } sct, err := logClient.AddChain(chain) if err != nil { log.Fatal(err) } // Display the SCT when := ctTimestampToTime(sct.Timestamp) fmt.Printf("%v: Uploaded chain of %d certs to %v log at %v\n", when, len(chain), sct.SCTVersion, *logURI) fmt.Printf("%v\n", signatureToString(&sct.Signature)) }
func main() { app := cmd.NewAppShell("boulder-publisher", "Submits issued certificates to CT logs") app.Action = func(c cmd.Config, stats metrics.Statter, logger blog.Logger) { logs := make([]*publisher.Log, len(c.Common.CT.Logs)) var err error for i, ld := range c.Common.CT.Logs { logs[i], err = publisher.NewLog(ld.URI, ld.Key) cmd.FailOnError(err, "Unable to parse CT log description") } if c.Common.CT.IntermediateBundleFilename == "" { logger.AuditErr("No CT submission bundle provided") os.Exit(1) } pemBundle, err := core.LoadCertBundle(c.Common.CT.IntermediateBundleFilename) cmd.FailOnError(err, "Failed to load CT submission bundle") bundle := []ct.ASN1Cert{} for _, cert := range pemBundle { bundle = append(bundle, ct.ASN1Cert(cert.Raw)) } pubi := publisher.New(bundle, logs, c.Publisher.SubmissionTimeout.Duration, logger) go cmd.DebugServer(c.Publisher.DebugAddr) go cmd.ProfileCmd("Publisher", stats) amqpConf := c.Publisher.AMQP pubi.SA, err = rpc.NewStorageAuthorityClient(clientName, amqpConf, stats) cmd.FailOnError(err, "Unable to create SA client") if c.Publisher.GRPC != nil { s, l, err := bgrpc.NewServer(c.Publisher.GRPC) cmd.FailOnError(err, "Failed to setup gRPC server") gw := bgrpc.NewPublisherServerWrapper(pubi) pubPB.RegisterPublisherServer(s, gw) go func() { err = s.Serve(l) cmd.FailOnError(err, "gRPC service failed") }() } pubs, err := rpc.NewAmqpRPCServer(amqpConf, c.Publisher.MaxConcurrentRPCServerRequests, stats, logger) cmd.FailOnError(err, "Unable to create Publisher RPC server") err = rpc.NewPublisherServer(pubs, pubi) cmd.FailOnError(err, "Unable to setup Publisher RPC server") err = pubs.Start(amqpConf) cmd.FailOnError(err, "Unable to run Publisher RPC server") } app.Run() }
// SubmitToCT will submit the certificate represented by certDER to any CT // logs configured in pub.CT.Logs (AMQP RPC method). func (pub *Impl) SubmitToCT(ctx context.Context, der []byte) error { cert, err := x509.ParseCertificate(der) if err != nil { pub.log.AuditErr(fmt.Sprintf("Failed to parse certificate: %s", err)) return err } localCtx, cancel := context.WithTimeout(ctx, pub.submissionTimeout) defer cancel() chain := append([]ct.ASN1Cert{der}, pub.issuerBundle...) for _, ctLog := range pub.ctLogs { sct, err := ctLog.client.AddChainWithContext(localCtx, chain) if err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 pub.log.AuditErr(fmt.Sprintf("Failed to submit certificate to CT log at %s: %s", ctLog.uri, err)) continue } err = ctLog.verifier.VerifySCTSignature(*sct, ct.LogEntry{ Leaf: ct.MerkleTreeLeaf{ LeafType: ct.TimestampedEntryLeafType, TimestampedEntry: ct.TimestampedEntry{ X509Entry: ct.ASN1Cert(der), EntryType: ct.X509LogEntryType, }, }, }) if err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 pub.log.AuditErr(fmt.Sprintf("Failed to verify SCT receipt: %s", err)) continue } internalSCT, err := sctToInternal(sct, core.SerialToString(cert.SerialNumber)) if err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 pub.log.AuditErr(fmt.Sprintf("Failed to convert SCT receipt: %s", err)) continue } err = pub.SA.AddSCTReceipt(localCtx, internalSCT) if err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 pub.log.AuditErr(fmt.Sprintf("Failed to store SCT receipt in database: %s", err)) continue } } return nil }
func setup(t *testing.T) (*Impl, *x509.Certificate, *ecdsa.PrivateKey) { intermediatePEM, _ := pem.Decode([]byte(testIntermediate)) pub := New(nil, nil, 0, log) pub.issuerBundle = append(pub.issuerBundle, ct.ASN1Cert(intermediatePEM.Bytes)) pub.SA = mocks.NewStorageAuthority(clock.NewFake()) leafPEM, _ := pem.Decode([]byte(testLeaf)) leaf, err := x509.ParseCertificate(leafPEM.Bytes) test.AssertNotError(t, err, "Couldn't parse leafPEM.Bytes") k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) test.AssertNotError(t, err, "Couldn't generate test key") return pub, leaf, k }
func createSignedSCT(leaf []byte, k *ecdsa.PrivateKey) string { rawKey, _ := x509.MarshalPKIXPublicKey(&k.PublicKey) pkHash := sha256.Sum256(rawKey) sct := ct.SignedCertificateTimestamp{ SCTVersion: ct.V1, LogID: pkHash, Timestamp: 1337, } serialized, _ := ct.SerializeSCTSignatureInput(sct, ct.LogEntry{ Leaf: ct.MerkleTreeLeaf{ LeafType: ct.TimestampedEntryLeafType, TimestampedEntry: ct.TimestampedEntry{ X509Entry: ct.ASN1Cert(leaf), EntryType: ct.X509LogEntryType, }, }, }) hashed := sha256.Sum256(serialized) var ecdsaSig struct { R, S *big.Int } ecdsaSig.R, ecdsaSig.S, _ = ecdsa.Sign(rand.Reader, k, hashed[:]) sig, _ := asn1.Marshal(ecdsaSig) ds := ct.DigitallySigned{ HashAlgorithm: ct.SHA256, SignatureAlgorithm: ct.ECDSA, Signature: sig, } var jsonSCTObj struct { SCTVersion ct.Version `json:"sct_version"` ID string `json:"id"` Timestamp uint64 `json:"timestamp"` Extensions string `json:"extensions"` Signature string `json:"signature"` } jsonSCTObj.SCTVersion = ct.V1 jsonSCTObj.ID = base64.StdEncoding.EncodeToString(pkHash[:]) jsonSCTObj.Timestamp = 1337 jsonSCTObj.Signature, _ = ds.Base64String() jsonSCT, _ := json.Marshal(jsonSCTObj) return string(jsonSCT) }
func main() { configFile := flag.String("config", "", "File path to the configuration file for this service") flag.Parse() if *configFile == "" { flag.Usage() os.Exit(1) } var c config err := cmd.ReadConfigFile(*configFile, &c) cmd.FailOnError(err, "Reading JSON config file into config structure") stats, logger := cmd.StatsAndLogging(c.Statsd, c.Syslog) scope := metrics.NewStatsdScope(stats, "Publisher") defer logger.AuditPanic() logger.Info(cmd.VersionString(clientName)) logs := make([]*publisher.Log, len(c.Common.CT.Logs)) for i, ld := range c.Common.CT.Logs { logs[i], err = publisher.NewLog(ld.URI, ld.Key) cmd.FailOnError(err, "Unable to parse CT log description") } if c.Common.CT.IntermediateBundleFilename == "" { logger.AuditErr("No CT submission bundle provided") os.Exit(1) } pemBundle, err := core.LoadCertBundle(c.Common.CT.IntermediateBundleFilename) cmd.FailOnError(err, "Failed to load CT submission bundle") bundle := []ct.ASN1Cert{} for _, cert := range pemBundle { bundle = append(bundle, ct.ASN1Cert(cert.Raw)) } amqpConf := c.Publisher.AMQP var sac core.StorageAuthority if c.Publisher.SAService != nil { conn, err := bgrpc.ClientSetup(c.Publisher.SAService, scope) cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to SA") sac = bgrpc.NewStorageAuthorityClient(sapb.NewStorageAuthorityClient(conn)) } else { sac, err = rpc.NewStorageAuthorityClient(clientName, amqpConf, scope) cmd.FailOnError(err, "Unable to create SA client") } pubi := publisher.New( bundle, logs, c.Publisher.SubmissionTimeout.Duration, logger, scope, sac) var grpcSrv *grpc.Server if c.Publisher.GRPC != nil { s, l, err := bgrpc.NewServer(c.Publisher.GRPC, scope) cmd.FailOnError(err, "Unable to setup Publisher gRPC server") gw := bgrpc.NewPublisherServerWrapper(pubi) pubPB.RegisterPublisherServer(s, gw) go func() { err = s.Serve(l) cmd.FailOnError(err, "Publisher gRPC service failed") }() grpcSrv = s } pubs, err := rpc.NewAmqpRPCServer(amqpConf, c.Publisher.MaxConcurrentRPCServerRequests, scope, logger) cmd.FailOnError(err, "Unable to create Publisher RPC server") go cmd.CatchSignals(logger, func() { pubs.Stop() if grpcSrv != nil { grpcSrv.GracefulStop() } }) err = rpc.NewPublisherServer(pubs, pubi) cmd.FailOnError(err, "Unable to setup Publisher RPC server") go cmd.DebugServer(c.Publisher.DebugAddr) go cmd.ProfileCmd(scope) err = pubs.Start(amqpConf) cmd.FailOnError(err, "Unable to run Publisher RPC server") }