   (mapv (partial str "DNS:")
         (utils/get-subject-dns-alt-names cert-or-csr)))
 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Writing various SSL objects safely
+;; These versions all encode writing atomically and knowledge of file permissions
+
+(schema/defn write-public-key
+  "Encode a key to PEM format and write it to a file atomically and with
+  appropriate permissions for a public key."
+  [key :- PublicKey
+   path :- schema/Str]
+  (ks-file/atomic-write path (partial utils/key->pem! key) public-key-perms))
+
+(schema/defn write-private-key
+  "Encode a key to PEM format and write it to a file atomically and with
+  appropriate permissions for a private key."
+  [key :- PrivateKey
+   path :- schema/Str]
+  (ks-file/atomic-write path (partial utils/key->pem! key) private-key-perms))
+
+(schema/defn write-cert
+  "Encode a certificate to PEM format and write it to a file atomically and with
+  appropriate permissions."
+  [cert :- Certificate
+   path :- schema/Str]
+  (ks-file/atomic-write path (partial utils/cert->pem! cert) public-key-perms))
+
+(schema/defn write-crl
+  "Encode a CRL to PEM format and write it to a file atomically and with
+  appropriate permissions."
+  [crl :- CertificateRevocationList
+   path :- schema/Str]
+  (ks-file/atomic-write path (partial utils/crl->pem! crl) public-key-perms))
+
+(schema/defn write-csr
+  "Encode a CSR to PEM format and write it to a file atomically and with
+  appropriate permissions."
+  [csr :- CertificateRequest
+   path :- schema/Str]
+  (ks-file/atomic-write path (partial utils/obj->pem! csr) public-key-perms))
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; Serial number functions + lock
 
 (def serial-file-lock
   "The lock used to prevent concurrent access to the serial number file."
   (new Object))
+
+(def serial-file-permissions
+  "rw-r--r--")
 
 (schema/defn parse-serial-number :- schema/Int
   "Parses a serial number from its format on disk.  See `format-serial-number`

  [serial-file :- schema/Str]
  (locking serial-file-lock
    (let [serial-number (get-serial-number! serial-file)]
      (spit serial-file (format-serial-number (inc serial-number)))
      serial-number)))

(schema/defn initialize-serial-file!
  "Initializes the serial number file on disk.  Serial numbers start at 1."
  [path :- schema/Str]
  (fs/create (fs/file path))
  (spit path (format-serial-number 1)))

(schema/defn write-local-cacrl! :- (schema/maybe Exception)
  "Spits the contents of 'cacrl' string to the 'localcacrl' file location if
  the 'cacrl' string contains valid CRL pem data. On success, return nil.
  On failure, return the Exception captured from the failed attempt to parse
  the CRL pem data."
  [localcacrl :- schema/Str
   cacrl :- schema/Str]
  (let [crl-reader (StringReader. cacrl)]
    (try
      (when (zero? (count (utils/pem->crls crl-reader)))
        (throw (CRLException. "No CRL data found")))
      (spit localcacrl cacrl)
      nil
      (catch IOException e
        e)

                        (i18n/tru "CSR ''{0}'' contains subject alternative names ({1}), which are disallowed." subject (str/join ", " dns-alt-names))
                        (i18n/tru "To allow subject alternative names, set allow-subject-alt-names to true in your puppetserver.conf file,")
                        (i18n/tru "restart the puppetserver, and try signing this certificate again."))})))))

(schema/defn ^:always-validate process-csr-submission!
  "Given a CSR for a subject (typically from the HTTP endpoint),
   perform policy checks and sign or save the CSR (based on autosign).
