...

Source file src/github.com/digitorus/pkcs7/encrypt.go

Documentation: github.com/digitorus/pkcs7

     1  package pkcs7
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/aes"
     6  	"crypto/cipher"
     7  	"crypto/des"
     8  	"crypto/rand"
     9  	"crypto/rsa"
    10  	"crypto/x509"
    11  	"crypto/x509/pkix"
    12  	"encoding/asn1"
    13  	"errors"
    14  	"fmt"
    15  )
    16  
    17  type envelopedData struct {
    18  	Version              int
    19  	RecipientInfos       []recipientInfo `asn1:"set"`
    20  	EncryptedContentInfo encryptedContentInfo
    21  }
    22  
    23  type encryptedData struct {
    24  	Version              int
    25  	EncryptedContentInfo encryptedContentInfo
    26  }
    27  
    28  type recipientInfo struct {
    29  	Version                int
    30  	IssuerAndSerialNumber  issuerAndSerial
    31  	KeyEncryptionAlgorithm pkix.AlgorithmIdentifier
    32  	EncryptedKey           []byte
    33  }
    34  
    35  type encryptedContentInfo struct {
    36  	ContentType                asn1.ObjectIdentifier
    37  	ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
    38  	EncryptedContent           asn1.RawValue `asn1:"tag:0,optional"`
    39  }
    40  
    41  const (
    42  	// EncryptionAlgorithmDESCBC is the DES CBC encryption algorithm
    43  	EncryptionAlgorithmDESCBC = iota
    44  
    45  	// EncryptionAlgorithmAES128CBC is the AES 128 bits with CBC encryption algorithm
    46  	// Avoid this algorithm unless required for interoperability; use AES GCM instead.
    47  	EncryptionAlgorithmAES128CBC
    48  
    49  	// EncryptionAlgorithmAES256CBC is the AES 256 bits with CBC encryption algorithm
    50  	// Avoid this algorithm unless required for interoperability; use AES GCM instead.
    51  	EncryptionAlgorithmAES256CBC
    52  
    53  	// EncryptionAlgorithmAES128GCM is the AES 128 bits with GCM encryption algorithm
    54  	EncryptionAlgorithmAES128GCM
    55  
    56  	// EncryptionAlgorithmAES256GCM is the AES 256 bits with GCM encryption algorithm
    57  	EncryptionAlgorithmAES256GCM
    58  )
    59  
    60  // ContentEncryptionAlgorithm determines the algorithm used to encrypt the
    61  // plaintext message. Change the value of this variable to change which
    62  // algorithm is used in the Encrypt() function.
    63  var ContentEncryptionAlgorithm = EncryptionAlgorithmDESCBC
    64  
    65  // ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt
    66  // content with an unsupported algorithm.
    67  var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC, AES-CBC, and AES-GCM supported")
    68  
    69  // ErrPSKNotProvided is returned when attempting to encrypt
    70  // using a PSK without actually providing the PSK.
    71  var ErrPSKNotProvided = errors.New("pkcs7: cannot encrypt content: PSK not provided")
    72  
    73  const nonceSize = 12
    74  
    75  type aesGCMParameters struct {
    76  	Nonce  []byte `asn1:"tag:4"`
    77  	ICVLen int
    78  }
    79  
    80  func encryptAESGCM(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) {
    81  	var keyLen int
    82  	var algID asn1.ObjectIdentifier
    83  	switch ContentEncryptionAlgorithm {
    84  	case EncryptionAlgorithmAES128GCM:
    85  		keyLen = 16
    86  		algID = OIDEncryptionAlgorithmAES128GCM
    87  	case EncryptionAlgorithmAES256GCM:
    88  		keyLen = 32
    89  		algID = OIDEncryptionAlgorithmAES256GCM
    90  	default:
    91  		return nil, nil, fmt.Errorf("invalid ContentEncryptionAlgorithm in encryptAESGCM: %d", ContentEncryptionAlgorithm)
    92  	}
    93  	if key == nil {
    94  		// Create AES key
    95  		key = make([]byte, keyLen)
    96  
    97  		_, err := rand.Read(key)
    98  		if err != nil {
    99  			return nil, nil, err
   100  		}
   101  	}
   102  
   103  	// Create nonce
   104  	nonce := make([]byte, nonceSize)
   105  
   106  	_, err := rand.Read(nonce)
   107  	if err != nil {
   108  		return nil, nil, err
   109  	}
   110  
   111  	// Encrypt content
   112  	block, err := aes.NewCipher(key)
   113  	if err != nil {
   114  		return nil, nil, err
   115  	}
   116  
   117  	gcm, err := cipher.NewGCM(block)
   118  	if err != nil {
   119  		return nil, nil, err
   120  	}
   121  
   122  	ciphertext := gcm.Seal(nil, nonce, content, nil)
   123  
   124  	// Prepare ASN.1 Encrypted Content Info
   125  	paramSeq := aesGCMParameters{
   126  		Nonce:  nonce,
   127  		ICVLen: gcm.Overhead(),
   128  	}
   129  
   130  	paramBytes, err := asn1.Marshal(paramSeq)
   131  	if err != nil {
   132  		return nil, nil, err
   133  	}
   134  
   135  	eci := encryptedContentInfo{
   136  		ContentType: OIDData,
   137  		ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{
   138  			Algorithm: algID,
   139  			Parameters: asn1.RawValue{
   140  				Tag:   asn1.TagSequence,
   141  				Bytes: paramBytes,
   142  			},
   143  		},
   144  		EncryptedContent: marshalEncryptedContent(ciphertext),
   145  	}
   146  
   147  	return key, &eci, nil
   148  }
   149  
   150  func encryptDESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) {
   151  	if key == nil {
   152  		// Create DES key
   153  		key = make([]byte, 8)
   154  
   155  		_, err := rand.Read(key)
   156  		if err != nil {
   157  			return nil, nil, err
   158  		}
   159  	}
   160  
   161  	// Create CBC IV
   162  	iv := make([]byte, des.BlockSize)
   163  	_, err := rand.Read(iv)
   164  	if err != nil {
   165  		return nil, nil, err
   166  	}
   167  
   168  	// Encrypt padded content
   169  	block, err := des.NewCipher(key)
   170  	if err != nil {
   171  		return nil, nil, err
   172  	}
   173  	mode := cipher.NewCBCEncrypter(block, iv)
   174  	plaintext, err := pad(content, mode.BlockSize())
   175  	if err != nil {
   176  		return nil, nil, err
   177  	}
   178  	cyphertext := make([]byte, len(plaintext))
   179  	mode.CryptBlocks(cyphertext, plaintext)
   180  
   181  	// Prepare ASN.1 Encrypted Content Info
   182  	eci := encryptedContentInfo{
   183  		ContentType: OIDData,
   184  		ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{
   185  			Algorithm:  OIDEncryptionAlgorithmDESCBC,
   186  			Parameters: asn1.RawValue{Tag: 4, Bytes: iv},
   187  		},
   188  		EncryptedContent: marshalEncryptedContent(cyphertext),
   189  	}
   190  
   191  	return key, &eci, nil
   192  }
   193  
   194  func encryptAESCBC(content []byte, key []byte) ([]byte, *encryptedContentInfo, error) {
   195  	var keyLen int
   196  	var algID asn1.ObjectIdentifier
   197  	switch ContentEncryptionAlgorithm {
   198  	case EncryptionAlgorithmAES128CBC:
   199  		keyLen = 16
   200  		algID = OIDEncryptionAlgorithmAES128CBC
   201  	case EncryptionAlgorithmAES256CBC:
   202  		keyLen = 32
   203  		algID = OIDEncryptionAlgorithmAES256CBC
   204  	default:
   205  		return nil, nil, fmt.Errorf("invalid ContentEncryptionAlgorithm in encryptAESCBC: %d", ContentEncryptionAlgorithm)
   206  	}
   207  
   208  	if key == nil {
   209  		// Create AES key
   210  		key = make([]byte, keyLen)
   211  
   212  		_, err := rand.Read(key)
   213  		if err != nil {
   214  			return nil, nil, err
   215  		}
   216  	}
   217  
   218  	// Create CBC IV
   219  	iv := make([]byte, aes.BlockSize)
   220  	_, err := rand.Read(iv)
   221  	if err != nil {
   222  		return nil, nil, err
   223  	}
   224  
   225  	// Encrypt padded content
   226  	block, err := aes.NewCipher(key)
   227  	if err != nil {
   228  		return nil, nil, err
   229  	}
   230  	mode := cipher.NewCBCEncrypter(block, iv)
   231  	plaintext, err := pad(content, mode.BlockSize())
   232  	if err != nil {
   233  		return nil, nil, err
   234  	}
   235  	cyphertext := make([]byte, len(plaintext))
   236  	mode.CryptBlocks(cyphertext, plaintext)
   237  
   238  	// Prepare ASN.1 Encrypted Content Info
   239  	eci := encryptedContentInfo{
   240  		ContentType: OIDData,
   241  		ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{
   242  			Algorithm:  algID,
   243  			Parameters: asn1.RawValue{Tag: 4, Bytes: iv},
   244  		},
   245  		EncryptedContent: marshalEncryptedContent(cyphertext),
   246  	}
   247  
   248  	return key, &eci, nil
   249  }
   250  
   251  // Encrypt creates and returns an envelope data PKCS7 structure with encrypted
   252  // recipient keys for each recipient public key.
   253  //
   254  // The algorithm used to perform encryption is determined by the current value
   255  // of the global ContentEncryptionAlgorithm package variable. By default, the
   256  // value is EncryptionAlgorithmDESCBC. To use a different algorithm, change the
   257  // value before calling Encrypt(). For example:
   258  //
   259  //     ContentEncryptionAlgorithm = EncryptionAlgorithmAES128GCM
   260  //
   261  // TODO(fullsailor): Add support for encrypting content with other algorithms
   262  func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) {
   263  	var eci *encryptedContentInfo
   264  	var key []byte
   265  	var err error
   266  
   267  	// Apply chosen symmetric encryption method
   268  	switch ContentEncryptionAlgorithm {
   269  	case EncryptionAlgorithmDESCBC:
   270  		key, eci, err = encryptDESCBC(content, nil)
   271  	case EncryptionAlgorithmAES128CBC:
   272  		fallthrough
   273  	case EncryptionAlgorithmAES256CBC:
   274  		key, eci, err = encryptAESCBC(content, nil)
   275  	case EncryptionAlgorithmAES128GCM:
   276  		fallthrough
   277  	case EncryptionAlgorithmAES256GCM:
   278  		key, eci, err = encryptAESGCM(content, nil)
   279  
   280  	default:
   281  		return nil, ErrUnsupportedEncryptionAlgorithm
   282  	}
   283  
   284  	if err != nil {
   285  		return nil, err
   286  	}
   287  
   288  	// Prepare each recipient's encrypted cipher key
   289  	recipientInfos := make([]recipientInfo, len(recipients))
   290  	for i, recipient := range recipients {
   291  		encrypted, err := encryptKey(key, recipient)
   292  		if err != nil {
   293  			return nil, err
   294  		}
   295  		ias, err := cert2issuerAndSerial(recipient)
   296  		if err != nil {
   297  			return nil, err
   298  		}
   299  		info := recipientInfo{
   300  			Version:               0,
   301  			IssuerAndSerialNumber: ias,
   302  			KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{
   303  				Algorithm: OIDEncryptionAlgorithmRSA,
   304  			},
   305  			EncryptedKey: encrypted,
   306  		}
   307  		recipientInfos[i] = info
   308  	}
   309  
   310  	// Prepare envelope content
   311  	envelope := envelopedData{
   312  		EncryptedContentInfo: *eci,
   313  		Version:              0,
   314  		RecipientInfos:       recipientInfos,
   315  	}
   316  	innerContent, err := asn1.Marshal(envelope)
   317  	if err != nil {
   318  		return nil, err
   319  	}
   320  
   321  	// Prepare outer payload structure
   322  	wrapper := contentInfo{
   323  		ContentType: OIDEnvelopedData,
   324  		Content:     asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent},
   325  	}
   326  
   327  	return asn1.Marshal(wrapper)
   328  }
   329  
   330  // EncryptUsingPSK creates and returns an encrypted data PKCS7 structure,
   331  // encrypted using caller provided pre-shared secret.
   332  func EncryptUsingPSK(content []byte, key []byte) ([]byte, error) {
   333  	var eci *encryptedContentInfo
   334  	var err error
   335  
   336  	if key == nil {
   337  		return nil, ErrPSKNotProvided
   338  	}
   339  
   340  	// Apply chosen symmetric encryption method
   341  	switch ContentEncryptionAlgorithm {
   342  	case EncryptionAlgorithmDESCBC:
   343  		_, eci, err = encryptDESCBC(content, key)
   344  
   345  	case EncryptionAlgorithmAES128GCM:
   346  		fallthrough
   347  	case EncryptionAlgorithmAES256GCM:
   348  		_, eci, err = encryptAESGCM(content, key)
   349  
   350  	default:
   351  		return nil, ErrUnsupportedEncryptionAlgorithm
   352  	}
   353  
   354  	if err != nil {
   355  		return nil, err
   356  	}
   357  
   358  	// Prepare encrypted-data content
   359  	ed := encryptedData{
   360  		Version:              0,
   361  		EncryptedContentInfo: *eci,
   362  	}
   363  	innerContent, err := asn1.Marshal(ed)
   364  	if err != nil {
   365  		return nil, err
   366  	}
   367  
   368  	// Prepare outer payload structure
   369  	wrapper := contentInfo{
   370  		ContentType: OIDEncryptedData,
   371  		Content:     asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent},
   372  	}
   373  
   374  	return asn1.Marshal(wrapper)
   375  }
   376  
   377  func marshalEncryptedContent(content []byte) asn1.RawValue {
   378  	asn1Content, _ := asn1.Marshal(content)
   379  	return asn1.RawValue{Tag: 0, Class: 2, Bytes: asn1Content, IsCompound: true}
   380  }
   381  
   382  func encryptKey(key []byte, recipient *x509.Certificate) ([]byte, error) {
   383  	if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil {
   384  		return rsa.EncryptPKCS1v15(rand.Reader, pub, key)
   385  	}
   386  	return nil, ErrUnsupportedAlgorithm
   387  }
   388  
   389  func pad(data []byte, blocklen int) ([]byte, error) {
   390  	if blocklen < 1 {
   391  		return nil, fmt.Errorf("invalid blocklen %d", blocklen)
   392  	}
   393  	padlen := blocklen - (len(data) % blocklen)
   394  	if padlen == 0 {
   395  		padlen = blocklen
   396  	}
   397  	pad := bytes.Repeat([]byte{byte(padlen)}, padlen)
   398  	return append(data, pad...), nil
   399  }
   400  

View as plain text