diff options
author | Andrew Cady <d@jerkface.net> | 2016-01-20 11:12:59 -0500 |
---|---|---|
committer | Andrew Cady <d@jerkface.net> | 2016-01-20 11:12:59 -0500 |
commit | ba128c5b8fedeb77e8d014b968884b570a698468 (patch) | |
tree | 18098e4f69f46cf57ea9eaa863f75efaefbceb67 | |
parent | 010742ccd357994b2784a862378e6329a19147d2 (diff) |
Generate signatures without external calls
-rw-r--r-- | acme.hs | 24 |
1 files changed, 15 insertions, 9 deletions
@@ -31,6 +31,9 @@ import Options.Applicative hiding (header) | |||
31 | import qualified Options.Applicative as Opt | 31 | import qualified Options.Applicative as Opt |
32 | import System.Directory | 32 | import System.Directory |
33 | import System.Process (readProcess) | 33 | import System.Process (readProcess) |
34 | import OpenSSL.EVP.Sign | ||
35 | import OpenSSL.EVP.Digest | ||
36 | import OpenSSL | ||
34 | 37 | ||
35 | directoryUrl :: String | 38 | directoryUrl :: String |
36 | directoryUrl = "https://acme-v01.api.letsencrypt.org/directory" | 39 | directoryUrl = "https://acme-v01.api.letsencrypt.org/directory" |
@@ -64,8 +67,8 @@ go :: CmdOpts -> IO () | |||
64 | go (CmdOpts privKeyFile domain email termOverride) = do | 67 | go (CmdOpts privKeyFile domain email termOverride) = do |
65 | let terms = fromMaybe defaultTerms termOverride | 68 | let terms = fromMaybe defaultTerms termOverride |
66 | doesFileExist privKeyFile >>= flip unless (genKey privKeyFile) | 69 | doesFileExist privKeyFile >>= flip unless (genKey privKeyFile) |
67 | userKey_ <- readFile privKeyFile >>= flip readPrivateKey PwTTY | 70 | priv <- readFile privKeyFile >>= flip readPrivateKey PwTTY |
68 | pub <- maybe (return Nothing) (rsaCopyPublic >=> return . Just) (toKeyPair userKey_ :: Maybe RSAKeyPair) | 71 | pub <- maybe (return Nothing) (rsaCopyPublic >=> return . Just) (toKeyPair priv :: Maybe RSAKeyPair) |
69 | case pub of | 72 | case pub of |
70 | Nothing -> error "Error: failed to parse RSA key." | 73 | Nothing -> error "Error: failed to parse RSA key." |
71 | Just (userKey :: RSAPubKey) -> do | 74 | Just (userKey :: RSAPubKey) -> do |
@@ -75,10 +78,10 @@ go (CmdOpts privKeyFile domain email termOverride) = do | |||
75 | let protected = b64 (header userKey nonce_) | 78 | let protected = b64 (header userKey nonce_) |
76 | 79 | ||
77 | -- Create user account | 80 | -- Create user account |
78 | forM_ email $ \m -> signPayload "registration" privKeyFile userKey protected (registration m terms) | 81 | forM_ email $ \m -> signPayload "registration" priv privKeyFile userKey protected (registration m terms) |
79 | 82 | ||
80 | -- Obtain a challenge | 83 | -- Obtain a challenge |
81 | signPayload "challenge-request" privKeyFile userKey protected (authz domain) | 84 | signPayload "challenge-request" priv privKeyFile userKey protected (authz domain) |
82 | 85 | ||
83 | -- Answser the challenge | 86 | -- Answser the challenge |
84 | let thumb = thumbprint (JWK (rsaE userKey) "RSA" (rsaN userKey)) | 87 | let thumb = thumbprint (JWK (rsaE userKey) "RSA" (rsaN userKey)) |
@@ -91,13 +94,13 @@ go (CmdOpts privKeyFile domain email termOverride) = do | |||
91 | putStrLn ("With content:\n" ++ BC.unpack thumbtoken) | 94 | putStrLn ("With content:\n" ++ BC.unpack thumbtoken) |
92 | 95 | ||
93 | -- Notify Let's Encrypt we answsered the challenge | 96 | -- Notify Let's Encrypt we answsered the challenge |
94 | signPayload "challenge-response" privKeyFile userKey protected (challenge thumbtoken) | 97 | signPayload "challenge-response" priv privKeyFile userKey protected (challenge thumbtoken) |
95 | 98 | ||
96 | -- Wait for challenge validation | 99 | -- Wait for challenge validation |
97 | 100 | ||
98 | -- Send a CSR and get a certificate | 101 | -- Send a CSR and get a certificate |
99 | csr_ <- B.readFile (domain ++ ".csr.der") | 102 | csr_ <- B.readFile (domain ++ ".csr.der") |
100 | signPayload "csr-request" privKeyFile userKey protected (csr csr_) | 103 | signPayload "csr-request" priv privKeyFile userKey protected (csr csr_) |
101 | 104 | ||
102 | data Directory = Directory { | 105 | data Directory = Directory { |
103 | _newCert :: String, | 106 | _newCert :: String, |
@@ -119,11 +122,14 @@ getNonce = fmap _nonce <$> getDirectory directoryUrl | |||
119 | 122 | ||
120 | -------------------------------------------------------------------------------- | 123 | -------------------------------------------------------------------------------- |
121 | -- | Sign and write a payload to a file with a nonce-protected header. | 124 | -- | Sign and write a payload to a file with a nonce-protected header. |
122 | signPayload :: RSAKey k => String -> String -> k -> ByteString -> ByteString -> IO () | 125 | signPayload :: (RSAKey k, KeyPair kp) => String -> kp -> String -> k -> ByteString -> ByteString -> IO () |
123 | signPayload name privKeyFile key protected payload = do | 126 | signPayload name priv privKeyFile key protected payload = withOpenSSL $ do |
124 | writePayload name protected payload | 127 | writePayload name protected payload |
125 | sig <- sign privKeyFile name | 128 | sig <- Main.sign privKeyFile name |
129 | Just dig <- getDigestByName "SHA256" | ||
130 | sig' <- signBS dig priv (B.concat [protected, ".", payload]) | ||
126 | writeBody name key protected payload sig | 131 | writeBody name key protected payload sig |
132 | writeBody (name ++ ".internal") key protected payload (b64 sig') | ||
127 | 133 | ||
128 | -- | Write a payload to file with a nonce-protected header. | 134 | -- | Write a payload to file with a nonce-protected header. |
129 | writePayload :: String -> ByteString -> ByteString -> IO () | 135 | writePayload :: String -> ByteString -> ByteString -> IO () |