Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Serialization of cryptographic data types

License

NotificationsYou must be signed in to change notification settings

ocheron/cryptostore

Repository files navigation

Build StatusBSDHaskell

This package allows to read and write cryptographic objects to/from ASN.1.

Currently the following is implemented:

  • Reading and writing private keys with optional encryption (this extendsx509-store API)

  • Reading and writing public keys, certificates and CRLs

  • PKCS #12 container format (password-based only)

  • Many parts of Cryptographic Message Syntax

Please have a look at the examples below as well as some warnings aboutcryptographic algorithms.

Private Keys

The API to read and write private keys is available in moduleCrypto.Store.PKCS8. When encrypting, some types and functions from moduleCrypto.Store.PKCS5 are also necessary.

Reading a private key from disk:

>:set-XOverloadedStrings>:mCrypto.Store.PKCS8> (key: _)<- readKeyFile"/path/to/privkey.pem"-- assuming single key> recover"mypassword" keyRight (PrivKeyRSA...)

Generating a private key and writing to disk, without encryption:

>:mCrypto.PubKey.RSACrypto.Store.PKCS8Data.X509> privKey<-PrivKeyRSA.snd<$> generate (2048`div`8)0x10001> writeKeyFilePKCS8Format"/path/to/privkey.pem" [privKey]

Generating a private key and writing to disk, with password-based encryption:

>:set-XOverloadedStrings>:mCrypto.PubKey.RSACrypto.Store.PKCS8Data.X509Crypto.Store.PKCS5> privKey<-PrivKeyRSA.snd<$> generate (2048`div`8)0x10001> salt<- generateSalt16>let kdf=PBKDF2 salt200000NothingPBKDF2_SHA256> encParams<- generateEncryptionParams (CBCAES256)>let pbes=PBES2 (PBES2Parameter kdf encParams)> writeEncryptedKeyFile"/path/to/privkey.pem" pbes"mypassword" privKeyRight()

Parameters used in this example are AES-256-CBC as cipher, PBKDF2 askey-derivation function, with a 16-byte salt, 200,000 iterations and SHA-256as pseudorandom function.

Public Keys and Signed Objects

ModuleCrypto.Store.X509 provides functions to read/write PEM files containingpublic keys, X.509 certificates and CRLs. These files are never encrypted.

Reading a public key and certificate from disk:

>:mData.X509Crypto.Store.X509> readPubKeyFile"/path/to/pubkey.pem"[PubKeyRSA...]> readSignedObject"/path/to/cert.pem"::IO [SignedCertificate][SignedExact...]

Writing back to disk:

>:mCrypto.Store.X509> writePubKeyFile"/path/to/pubkey.pem" [pubKey]> writeSignedObject"/path/to/cert.pem" [cert]

PKCS #12

PKCS #12 is a complex format with multiple layers of protection, providingusually both privacy and integrity, with a single password for all or not. TheAPI to read PKCS #12 files requires some password at each layer. This API isavailable in moduleCrypto.Store.PKCS12.

Reading a binary PKCS #12 file using a single password doing both integrityand privacy (usual case):

>:set-XOverloadedStrings>:mCrypto.Store.PKCS12>Right p12<- readP12File"/path/to/file.p12">letRight (password, pkcs12)= recoverAuthenticated"mypassword" p12>letRight contents= recover password (unPKCS12 pkcs12)> getAllSafeX509Certs contents[SignedExact {getSigned=...}]> recover password (getAllSafeKeys contents)Right [PrivKeyRSA...]

Reading a binary PKCS #12 file using distinct integrity and privacy passwords:

>:set-XOverloadedStrings>:mCrypto.Store.PKCS12>Right p12<- readP12File"/path/to/file.p12">letRight (_, pkcs12)= recoverAuthenticated"myintegritypassword" p12>letRight contents= recover"myprivacypassword" (unPKCS12 pkcs12)> getAllSafeX509Certs contents[SignedExact {getSigned=...}]> recover"myprivacypassword" (getAllSafeKeys contents)Right [PrivKeyRSA...]

Generating a PKCS #12 file containing a private key:

>:set-XOverloadedStrings-- Generate a private key>:mCrypto.PubKey.RSAData.X509> privKey<-PrivKeyRSA.snd<$> generate (2048`div`8)0x10001-- Put the key inside a bag>:mCrypto.Store.PKCS12Crypto.Store.PKCS8Crypto.Store.PKCS5Crypto.Store.CMS>let attrs= setFriendlyName"Some Key"[]>     keyBag=Bag (KeyBag$FormattedKeyPKCS8Format privKey) attrs>     contents=SafeContents [keyBag]-- Encrypt the contents> salt<- generateSalt16>let kdf=PBKDF2 salt200000NothingPBKDF2_SHA256> encParams<- generateEncryptionParams (CBCAES256)>let pbes=PBES2 (PBES2Parameter kdf encParams)>Right pkcs12= encrypted pbes"mypassword" contents-- Save to PKCS #12 with integrity protection (same password)> salt'<- generateSalt16>let iParams=TraditionalIntegrity (DigestAlgorithmSHA256) (PBEParameter salt'200000)> writeP12File"/path/to/privkey.p12" iParams"mypassword" pkcs12Right()

The API also provides functions to generate/extract a pair containing a privatekey and a certificate chain. This pair is the type aliasCredential intls.

>:set-XOverloadedStrings>:mCrypto.Store.PKCS12Crypto.Store.PKCS8Crypto.Store.PKCS5Crypto.Store.CMS-- Read PKCS #12 content as credential>Right p12<- readP12File"/path/to/file.p12">letRight (_, pkcs12)= recoverAuthenticated"myintegritypassword" p12>letRight (Just cred)= recover"myprivacypassword" (toCredential pkcs12)> cred(CertificateChain [...],PrivKeyRSA(...))-- Scheme to reencrypt the key> saltK<- generateSalt16>let kdfK=PBKDF2 saltK200000NothingPBKDF2_SHA256> encParamsK<- generateEncryptionParams (CBCAES256)>let sKey=PBES2 (PBES2Parameter kdfK encParamsK)-- Scheme to reencrypt the certificate chain> saltC<- generateSalt8>let kdfC=PBKDF2 saltC100000NothingPBKDF2_SHA256> encParamsC<- generateEncryptionParams (CBCAES128)>let sCert=PBES2 (PBES2Parameter kdfC encParamsC)-- Write the content back to a new file>letRight pkcs12'= fromCredential (Just sCert) sKey"myprivacypassword" cred> salt<- generateSalt16>let iParams=TraditionalIntegrity (DigestAlgorithmSHA256) (PBEParameter salt200000)> writeP12File"/path/to/newfile.p12" iParams"myintegritypassword" pkcs12'

VariantstoNamedCredential andfromNamedCredential are also available whenPKCS #12 elements need an alias (friendly name).

The library also supports integrity protection with PBMAC1 as defined inRFC 9579. The following example showshow to use PBKDF2 with SHA-256 HMAC and PRF:

>:set-XOverloadedStrings-- Generate a private key>:mCrypto.PubKey.RSAData.X509> privKey<-PrivKeyRSA.snd<$> generate (2048`div`8)0x10001-- Put the key inside a bag>:mCrypto.Store.PKCS12Crypto.Store.PKCS8Crypto.Store.PKCS5Crypto.Store.CMS>let attrs= setFriendlyName"Some Key"[]>     keyBag=Bag (KeyBag$FormattedKeyPKCS8Format privKey) attrs>     contents=SafeContents [keyBag]-- Encrypt the contents> salt<- generateSalt16>let kdf=PBKDF2 salt200000NothingPBKDF2_SHA256> encParams<- generateEncryptionParams (CBCAES256)>let pbes=PBES2 (PBES2Parameter kdf encParams)>Right pkcs12= encrypted pbes"mypassword" contents-- Save to PKCS #12 with PBMAC1 integrity protection (same password)> salt'<- generateSalt16>let kdf'=PBKDF2 salt'200000 (Just32)PBKDF2_SHA256>let authScheme=PBMAC1$PBMAC1Parameter kdf' (HMACSHA256)>let iParams=AuthSchemeIntegrity authScheme> writeP12File"/path/to/privkey.p12" iParams"mypassword" pkcs12Right()

Cryptographic Message Syntax

The API to read and write CMS content is available inCrypto.Store.CMS. Themain data typeContentInfo represents a CMS structure.

Implemented content types are:

  • data
  • signed data
  • enveloped data
  • digested data
  • encrypted data
  • authenticated data
  • and authenticated-enveloped data

Notable omissions:

  • streaming
  • compressed data
  • and S/MIME external format (only PEM is supported, i.e. the textual encodingofRFC 7468)

Enveloped data

The following examples generate a CMS structure enveloping some data to apassword recipient, then decrypt the data to recover the content.

Generating enveloped data

>:set-XOverloadedStrings>:mCrypto.Store.CMS-- Input content info>let info=DataCI"Hi, what will you need from the cryptostore?"-- Content encryption will use AES-128-CBC> ceParams<- generateEncryptionParams (CBCAES128)> ceKey<- generateKey ceParams::IOContentEncryptionKey-- Encrypt the Content Encryption Key with a Password Recipient Info,-- i.e. a KDF will derive the Key Encryption Key from a password-- that the recipient will need to know> salt<- generateSalt16>let kdf=PBKDF2 salt200000NothingPBKDF2_SHA256> keParams<- generateEncryptionParams (CBCAES128)>let pri= forPasswordRecipient"mypassword" kdf (PWRIKEK keParams)-- Generate the enveloped structure for this single recipient.  Encrypted-- content is kept attached in the structure.>Right envelopedData<- envelopDatamempty ceKey ceParams [pri][] info>let envelopedCI= toAttachedCI envelopedData> writeCMSFile"/path/to/enveloped.pem" [envelopedCI]

Opening the enveloped data

>:set-XOverloadedStrings>:mCrypto.Store.CMS-- Then this recipient just has to read the file and recover enveloped-- content using the password> [EnvelopedDataCI envelopedEncapData]<- readCMSFile"/path/to/enveloped.pem"> envelopedData<- fromAttached envelopedEncapData> openEnvelopedData (withRecipientPassword"mypassword") envelopedDataRight (DataCI"Hi, what will you need from the cryptostore?")

Signed data

The following examples generate a CMS structure signing data with an RSA keyand certificate, then verify the signature and recover the content.

Signing data

>:set-XOverloadedStrings>:mCrypto.Store.CMSData.X509Crypto.Store.X509Crypto.Store.PKCS8-- Input content info>let info=DataCI"Some trustworthy content"-- Read signer certificate and private key> (key: _)<- readKeyFile"/path/to/privkey.pem"-- assuming single key>letRight priv= recover"mypassword" key> chain<- readSignedObject"/path/to/cert.pem"::IO [SignedCertificate]>let cert=CertificateChain chain-- Signature will use RSASSA-PSS and SHA-256>let sha256=DigestAlgorithmSHA256>let params=PSSParams sha256 (MGF1 sha256)16-- Generate the signed structure with a single signer.  Signed content is-- kept attached in the structure.>let signer= certSigner (RSAPSS params) priv cert (Just[])[]>Right signedData<- signData [signer] info>let signedCI= toAttachedCI signedData> writeCMSFile"/path/to/signed.pem" [signedCI]

Verifying signed data

-- Read certificate authorities to be trusted for validation>:mCrypto.Store.X509Data.X509.CertificateStore> store<- makeCertificateStore<$> readSignedObject"/path/to/cacert.pem"-- Assume we will not verify the signer FQHN.  Instead the certificate could be-- related to an identity from which we received the signed data.>:mData.Default.ClassData.X509Data.X509.Validation>let validateNoFQHN= validateHashSHA256 def def { checkFQHN=False }>let noServiceID= (undefined,undefined)-- Read the signed data and validate it to recover the content>:mCrypto.Store.CMSData.Default.Class> [SignedDataCI signedEncapData]<- readCMSFile"/path/to/signed.pem"> signedData<- fromAttached signedEncapData>let doValidation _ chain=null<$> validateNoFQHN store def noServiceID chain> verifySignedData (withSignerCertificate doValidation) signedDataRight (DataCI"Some trustworthy content")

Algorithms and security

For compatibility reasons cryptostore implements many outdated algorithms thatare still in use in data formats. Please check your security requirements. Newapplications should favor PBKDF2 or Scrypt and AEAD ciphers.

Additionally, the package is designed exclusively for store and forwardscenarios, as most algorithms will not be perfectly safe for interactive use.ECDSA signature generation uses the generic ECC implementation from cryptoniteand could leak the private key under timing attack. A padding oracle onCBC-encrypted ciphertext allows to recover the plaintext.

Design

Main dependencies are:

  • cryptonite implementation ofpublic-key systems, symmetric ciphers, KDFs, MAC, and one-way hash functions
  • asn1-types andasn1-encoding to encodeand decode ASN.1 content
  • pem to read and write PEM files
  • x509 contains the certificate andprivate-key data types

Internally the ASN.1 parser used is a local implementation extending the code ofasn1-parse. This extension isable to parseASN1Repr, i.e. a stream of ASN.1 tags associated with the binarydecoding events the tags were originated from. Similarly generation of ASN.1content does not use theASN1S type but an extension which is able to encode astream where some parts have already been encoded. Retaining the originalBER/DER encoding is required when incorporating MACed or signed content.


[8]ページ先頭

©2009-2025 Movatter.jp