summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2016-01-21 01:46:06 -0500
committerAndrew Cady <d@jerkface.net>2016-01-21 01:48:47 -0500
commitd313b531e9f83878face52c33955f477db164e1f (patch)
treebb991d836a48e9ae35d00bdc6dcffd95e02d98ec
parentc7a9033041a0b6eb4006cb195a84233577f817e3 (diff)
Generate RSA keys and CSRs using HsOpenSSL
Unfortunately, an external process is still needed to convert x509 CSRs from PEM to DER.
-rw-r--r--acme-encrypt.cabal2
-rw-r--r--acme.hs33
2 files changed, 25 insertions, 10 deletions
diff --git a/acme-encrypt.cabal b/acme-encrypt.cabal
index 4d3aa44..66b80ec 100644
--- a/acme-encrypt.cabal
+++ b/acme-encrypt.cabal
@@ -24,7 +24,7 @@ executable acme-encrypt-exe
24 build-depends: base, 24 build-depends: base,
25 cryptonite, aeson, bytestring, base64-bytestring, SHA, 25 cryptonite, aeson, bytestring, base64-bytestring, SHA,
26 text, HsOpenSSL, process, wreq, lens, lens-aeson, 26 text, HsOpenSSL, process, wreq, lens, lens-aeson,
27 optparse-applicative, directory, mtl 27 optparse-applicative, directory, mtl, process-extras
28 -- , acme-encrypt 28 -- , acme-encrypt
29 default-language: Haskell2010 29 default-language: Haskell2010
30 30
diff --git a/acme.hs b/acme.hs
index 230bc27..a4751c2 100644
--- a/acme.hs
+++ b/acme.hs
@@ -41,10 +41,11 @@ import OpenSSL.EVP.PKey
41import OpenSSL.EVP.Sign 41import OpenSSL.EVP.Sign
42import OpenSSL.PEM 42import OpenSSL.PEM
43import OpenSSL.RSA 43import OpenSSL.RSA
44import OpenSSL.X509.Request
44import Options.Applicative hiding (header) 45import Options.Applicative hiding (header)
45import qualified Options.Applicative as Opt 46import qualified Options.Applicative as Opt
46import System.Directory 47import System.Directory
47import System.Process (readProcess) 48import System.Process.ByteString
48 49
49stagingDirectoryUrl, liveDirectoryUrl :: String 50stagingDirectoryUrl, liveDirectoryUrl :: String
50liveDirectoryUrl = "https://acme-v01.api.letsencrypt.org/directory" 51liveDirectoryUrl = "https://acme-v01.api.letsencrypt.org/directory"
@@ -77,12 +78,24 @@ cmdopts = CmdOpts <$> strOption (long "key" <> metavar "FILE" <> help "filename
77 <*> switch (long "staging" <> help "use staging servers instead of live servers (certificates will not be real!)") 78 <*> switch (long "staging" <> help "use staging servers instead of live servers (certificates will not be real!)")
78 79
79genKey :: String -> IO () 80genKey :: String -> IO ()
80genKey privKeyFile = void $ readProcess "openssl" (words "genrsa -out" ++ [privKeyFile, "4096"]) "" 81genKey privKeyFile = withOpenSSL $ do
81 82 kp <- generateRSAKey' 4096 65537
82genReq :: String -> String -> String -> IO () 83 pem <- writePKCS8PrivateKey kp Nothing
83genReq privKeyFile domain out = void $ readProcess "openssl" (args privKeyFile domain out) "" 84 writeFile privKeyFile pem
84 where 85
85 args k d o = words "req -new -sha256 -outform DER -key" ++ [k, "-subj", "/CN=" ++ d, "-out", o] 86genReq :: FilePath -> String -> IO ByteString
87genReq domainKeyFile domain = withOpenSSL $ do
88 (Keys priv pub) <- readKeys domainKeyFile
89 Just dig <- getDigestByName "SHA256"
90 req <- newX509Req
91 setSubjectName req [("CN", domain)]
92 setVersion req 0
93 setPublicKey req pub
94 signX509Req req priv (Just dig)
95 pem <- writeX509Req req ReqNewFormat
96 -- Sigh. No DER support for X509 reqs in HsOpenSSL.
97 (_, o, _) <- readProcessWithExitCode "openssl" (words "req -outform der") (encodeUtf8 $ T.pack pem)
98 return o
86 99
87data Keys = Keys SomeKeyPair RSAPubKey 100data Keys = Keys SomeKeyPair RSAPubKey
88readKeys :: String -> IO Keys 101readKeys :: String -> IO Keys
@@ -108,11 +121,13 @@ go (CmdOpts privKeyFile domain challengeDir email termOverride staging) = do
108 121
109 doesDirectoryExist domain >>= flip unless (createDirectory domain) 122 doesDirectoryExist domain >>= flip unless (createDirectory domain)
110 doesFileExist domainKeyFile >>= flip unless (genKey domainKeyFile) 123 doesFileExist domainKeyFile >>= flip unless (genKey domainKeyFile)
111 doesFileExist domainCSRFile >>= flip unless (genReq domainKeyFile domain domainCSRFile)
112 124
113 csrData <- B.readFile domainCSRFile
114 keys <- readKeys privKeyFile 125 keys <- readKeys privKeyFile
115 126
127 doesFileExist domainCSRFile >>= flip unless (genReq domainKeyFile domain >>= B.writeFile domainCSRFile)
128
129 csrData <- B.readFile domainCSRFile
130
116 -- TODO: verify that challengeDir is writable before continuing. 131 -- TODO: verify that challengeDir is writable before continuing.
117 132
118 runACME directoryUrl keys $ do 133 runACME directoryUrl keys $ do