diff --git a/Data/OpenPGP.hs b/Data/OpenPGP.hs
--- a/Data/OpenPGP.hs
+++ b/Data/OpenPGP.hs
@@ -3,7 +3,7 @@
 -- The recommended way to import this module is:
 --
 -- > import qualified Data.OpenPGP as OpenPGP
-module Data.OpenPGP (Message(..), Packet(..), SignatureSubpacket(..), HashAlgorithm(..), KeyAlgorithm(..), CompressionAlgorithm(..), MPI(..), fingerprint_material, signatures_and_data, signature_issuer) where
+module Data.OpenPGP (Message(..), Packet(..), SignatureSubpacket(..), HashAlgorithm(..), KeyAlgorithm(..), CompressionAlgorithm(..), MPI(..), fingerprint_material, signatures_and_data, signature_issuer, calculate_signature_trailer) where
 
 import Control.Monad
 import Data.Bits
@@ -403,6 +403,15 @@
 	]
 	where material = LZ.concat $
 		map (\f -> encode (key ! f)) (public_key_fields algorithm)
+-- Proxy to make SecretKeyPacket work
+fingerprint_material (SecretKeyPacket {version = 4,
+                      timestamp = timestamp,
+                      key_algorithm = algorithm,
+                      key = key}) =
+	fingerprint_material PublicKeyPacket {version = 4,
+                      timestamp = timestamp,
+                      key_algorithm = algorithm,
+                      key = key}
 fingerprint_material p | version p `elem` [2, 3] = [n, e]
 	where n = LZ.drop 2 (encode (key p ! 'n'))
 	      e = LZ.drop 2 (encode (key p ! 'e'))
@@ -537,7 +546,7 @@
 				return len
 		tag <- get :: Get Word8
 		-- This forces the whole packet to be consumed
-		packet <- getLazyByteString len
+		packet <- getLazyByteString (len-1)
 		return $ runGet (parse_signature_subpacket tag) packet
 
 -- | Find the keyid that issued a SignaturePacket
diff --git a/Data/OpenPGP/Crypto.hs b/Data/OpenPGP/Crypto.hs
--- a/Data/OpenPGP/Crypto.hs
+++ b/Data/OpenPGP/Crypto.hs
@@ -4,13 +4,15 @@
 -- The recommended way to import this module is:
 --
 -- > import qualified Data.OpenPGP.Crypto as OpenPGP
-module Data.OpenPGP.Crypto (verify, fingerprint) where
+module Data.OpenPGP.Crypto (sign, verify, fingerprint) where
 
 import Data.Word
+import Data.List (find)
 import Data.Map ((!))
 import qualified Data.ByteString.Lazy as LZ
 
 import Data.Binary
+import Codec.Utils (fromOctets)
 import qualified Codec.Encryption.RSA as RSA
 import qualified Data.Digest.MD5 as MD5
 import qualified Data.Digest.SHA1 as SHA1
@@ -91,3 +93,48 @@
 	sig = sigs !! sigidx
 	(sigs, (OpenPGP.LiteralDataPacket {OpenPGP.content = dta}):_) =
 		OpenPGP.signatures_and_data message
+
+-- | Sign a message.  Only supports RSA keys for now.
+sign :: OpenPGP.Message    -- ^ SecretKeys, one of which will be used
+        -> OpenPGP.Message -- ^ Message containing LiteralData to sign
+        -> OpenPGP.HashAlgorithm -- ^ HashAlgorithm to use is signature
+        -> String  -- ^ KeyID of key to choose or @[]@ for first
+        -> Integer -- ^ Timestamp to put in signature
+        -> OpenPGP.Message
+sign keys message hsh keyid timestamp =
+	OpenPGP.Message $ (sig' {
+		OpenPGP.signature = OpenPGP.MPI $ toNum $ reverse final,
+		OpenPGP.hash_head = toNum $ take 2 final
+	}):m
+	where
+	-- toNum has explicit param so that it can remain polymorphic
+	toNum l = fromOctets (256::Integer) l
+	final   = RSA.decrypt (n, d) encoded
+	encoded = emsa_pkcs1_v1_5_encode dta (length n) hsh
+	(n, d)  = (keyfield_as_octets k 'n', keyfield_as_octets k 'd')
+	dta     = LZ.unpack $ LZ.append
+		(OpenPGP.content dataP) (OpenPGP.trailer sig')
+	sig'    = sig {
+		OpenPGP.trailer = OpenPGP.calculate_signature_trailer sig
+	}
+	sig     = OpenPGP.SignaturePacket {
+		OpenPGP.version = 4,
+		OpenPGP.key_algorithm = OpenPGP.RSA,
+		OpenPGP.hash_algorithm = hsh,
+		OpenPGP.signature_type = stype,
+		OpenPGP.hashed_subpackets = [
+			OpenPGP.SignatureCreationTimePacket $ fromIntegral timestamp,
+			OpenPGP.IssuerPacket keyid'
+		],
+		OpenPGP.unhashed_subpackets = [],
+		OpenPGP.signature = undefined,
+		OpenPGP.trailer = undefined,
+		OpenPGP.hash_head = undefined
+	}
+	keyid'  = reverse $ take 16 $ reverse $ fingerprint k
+	stype   = if OpenPGP.format dataP == 'b' then 0x00 else 0x01
+	Just k  = find_key keys keyid
+	Just dataP = find isLiteralData m
+	OpenPGP.Message m = message
+	isLiteralData (OpenPGP.LiteralDataPacket {}) = True
+	isLiteralData _                              = False
diff --git a/README b/README
--- a/README
+++ b/README
@@ -1,12 +1,18 @@
-OpenPGP-Haskell
-
-This is an OpenPGP library inspired by my work on OpenPGP-PHP <http://github.com/bendiken/openpgp-php>.
+This is an OpenPGP library inspired by my work on OpenPGP libraries in
+Ruby <https://github.com/singpolyma/openpgp>,
+PHP <http://github.com/singpolyma/openpgp-php>,
+and Python <https://github.com/singpolyma/OpenPGP-Python>.
 
-It defines types Message and Packet to represent OpenPGP messages as series of packets and then defines instances of Data.Binary for each to facilitate encoding/decoding.
+It defines types to represent OpenPGP messages as a series of packets
+and then defines instances of Data.Binary for each to facilitate
+encoding/decoding.
 
-There is also a wrapper around <http://hackage.haskell.org/package/Crypto> that currently does fingerprint generation and signature verification.
+There is also a wrapper around <http://hackage.haskell.org/package/Crypto>
+that currently does fingerprint generation, signature generation, and
+signature verification (for RSA keys only).
 
-It is intended that you use qualified imports with this library.  If importing both modules, something like this will do:
+It is intended that you use qualified imports with this library.  If importing
+both modules, something like this will do:
 
 > import qualified Data.OpenPGP as OpenPGP
 > import qualified Data.OpenPGP.Crypto as OpenPGP
diff --git a/openpgp.cabal b/openpgp.cabal
--- a/openpgp.cabal
+++ b/openpgp.cabal
@@ -1,5 +1,5 @@
 name:            openpgp
-version:         0.1
+version:         0.2
 cabal-version:   >= 1.8
 license:         OtherLicense
 license-file:    COPYING
@@ -13,15 +13,22 @@
 homepage:        http://github.com/singpolyma/OpenPGP-Haskell
 bug-reports:     http://github.com/singpolyma/OpenPGP-Haskell/issues
 build-type:      Simple
-description:     This is an OpenPGP library inspired by my work on OpenPGP-PHP <http://github.com/bendiken/openpgp-php>.
-        It defines types Message and Packet to represent OpenPGP messages as series
-        of packets and then defines instances of Data.Binary for each to facilitate
+description:
+        This is an OpenPGP library inspired by my work on OpenPGP libraries in
+        Ruby <https://github.com/singpolyma/openpgp>,
+        PHP <http://github.com/singpolyma/openpgp-php>,
+        and Python <https://github.com/singpolyma/OpenPGP-Python>.
+        .
+        It defines types to represent OpenPGP messages as a series of packets
+        and then defines instances of Data.Binary for each to facilitate
         encoding/decoding.
         .
         There is also a wrapper around <http://hackage.haskell.org/package/Crypto>
-        that currently does fingerprint generation and signature verification.
+        that currently does fingerprint generation, signature generation, and
+        signature verification (for RSA keys only).
         .
-        It is intended that you use qualified imports with this library.  If importing both modules, something like this will do:
+        It is intended that you use qualified imports with this library.  If importing
+        both modules, something like this will do:
         .
         > import qualified Data.OpenPGP as OpenPGP
         > import qualified Data.OpenPGP.Crypto as OpenPGP
