github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/x509/pem_decrypt.go (about)

     1  // Copyright 2012 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package x509
     6  
     7  /*
     8  x509/pem_decrypt.go 对pem块做加密的实现。
     9  参考标准: RFC 1423
    10  其中派生密钥的算法参考的是OpenSSL的实现。
    11  */
    12  
    13  // RFC 1423 describes the encryption of PEM blocks. The algorithm used to
    14  // generate a key from the password was derived by looking at the OpenSSL
    15  // implementation.
    16  
    17  import (
    18  	"crypto/aes"
    19  	"crypto/cipher"
    20  	"crypto/des"
    21  	"crypto/md5"
    22  	"encoding/hex"
    23  	"encoding/pem"
    24  	"errors"
    25  	"io"
    26  	"strings"
    27  
    28  	"github.com/hxx258456/ccgo/sm4"
    29  )
    30  
    31  type PEMCipher int
    32  
    33  // Possible values for the EncryptPEMBlock encryption algorithm.
    34  const (
    35  	_ PEMCipher = iota
    36  	PEMCipherDES
    37  	PEMCipher3DES
    38  	PEMCipherAES128
    39  	PEMCipherAES192
    40  	PEMCipherAES256
    41  	PEMCipherSM4
    42  )
    43  
    44  // rfc1423Algo holds a method for enciphering a PEM block.
    45  type rfc1423Algo struct {
    46  	cipher     PEMCipher
    47  	name       string
    48  	cipherFunc func(key []byte) (cipher.Block, error)
    49  	keySize    int
    50  	blockSize  int
    51  }
    52  
    53  // rfc1423Algos holds a slice of the possible ways to encrypt a PEM
    54  // block. The ivSize numbers were taken from the OpenSSL source.
    55  var rfc1423Algos = []rfc1423Algo{{
    56  	cipher:     PEMCipherDES,
    57  	name:       "DES-CBC",
    58  	cipherFunc: des.NewCipher,
    59  	keySize:    8,
    60  	blockSize:  des.BlockSize,
    61  }, {
    62  	cipher:     PEMCipher3DES,
    63  	name:       "DES-EDE3-CBC",
    64  	cipherFunc: des.NewTripleDESCipher,
    65  	keySize:    24,
    66  	blockSize:  des.BlockSize,
    67  }, {
    68  	cipher:     PEMCipherAES128,
    69  	name:       "AES-128-CBC",
    70  	cipherFunc: aes.NewCipher,
    71  	keySize:    16,
    72  	blockSize:  aes.BlockSize,
    73  }, {
    74  	cipher:     PEMCipherAES192,
    75  	name:       "AES-192-CBC",
    76  	cipherFunc: aes.NewCipher,
    77  	keySize:    24,
    78  	blockSize:  aes.BlockSize,
    79  }, {
    80  	cipher:     PEMCipherAES256,
    81  	name:       "AES-256-CBC",
    82  	cipherFunc: aes.NewCipher,
    83  	keySize:    32,
    84  	blockSize:  aes.BlockSize,
    85  }, {
    86  	// 添加国密SM4的对应实现
    87  	cipher:     PEMCipherSM4,
    88  	name:       "SM4-128-CBC",
    89  	cipherFunc: sm4.NewCipher,
    90  	keySize:    16,
    91  	blockSize:  sm4.BlockSize,
    92  },
    93  }
    94  
    95  // deriveKey uses a key derivation function to stretch the password into a key
    96  // with the number of bits our cipher requires. This algorithm was derived from
    97  // the OpenSSL source.
    98  func (c rfc1423Algo) deriveKey(password, salt []byte) []byte {
    99  	hash := md5.New()
   100  	out := make([]byte, c.keySize)
   101  	var digest []byte
   102  
   103  	for i := 0; i < len(out); i += len(digest) {
   104  		hash.Reset()
   105  		hash.Write(digest)
   106  		hash.Write(password)
   107  		hash.Write(salt)
   108  		digest = hash.Sum(digest[:0])
   109  		copy(out[i:], digest)
   110  	}
   111  	return out
   112  }
   113  
   114  // IsEncryptedPEMBlock returns whether the PEM block is password encrypted
   115  // according to RFC 1423.
   116  func IsEncryptedPEMBlock(b *pem.Block) bool {
   117  	_, ok := b.Headers["DEK-Info"]
   118  	return ok
   119  }
   120  
   121  // ErrorIncorrectPassword is returned when an incorrect password is detected.
   122  var ErrorIncorrectPassword = errors.New("x509: decryption password incorrect")
   123  
   124  // DecryptPEMBlock takes a PEM block encrypted according to RFC 1423 and the
   125  // password used to encrypt it and returns a slice of decrypted DER encoded
   126  // bytes. It inspects the DEK-Info header to determine the algorithm used for
   127  // decryption. If no DEK-Info header is present, an error is returned. If an
   128  // incorrect password is detected an ErrorIncorrectPassword is returned. Because
   129  // of deficiencies in the format, it's not always possible to detect an
   130  // incorrect password. In these cases no error will be returned but the
   131  // decrypted DER bytes will be random noise.
   132  func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) {
   133  	dek, ok := b.Headers["DEK-Info"]
   134  	if !ok {
   135  		return nil, errors.New("x509: no DEK-Info header in block")
   136  	}
   137  
   138  	idx := strings.Index(dek, ",")
   139  	if idx == -1 {
   140  		return nil, errors.New("x509: malformed DEK-Info header")
   141  	}
   142  
   143  	mode, hexIV := dek[:idx], dek[idx+1:]
   144  	ciph := cipherByName(mode)
   145  	if ciph == nil {
   146  		return nil, errors.New("x509: unknown encryption mode")
   147  	}
   148  	iv, err := hex.DecodeString(hexIV)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	if len(iv) != ciph.blockSize {
   153  		return nil, errors.New("x509: incorrect IV size")
   154  	}
   155  
   156  	// Based on the OpenSSL implementation. The salt is the first 8 bytes
   157  	// of the initialization vector.
   158  	key := ciph.deriveKey(password, iv[:8])
   159  	block, err := ciph.cipherFunc(key)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	if len(b.Bytes)%block.BlockSize() != 0 {
   165  		return nil, errors.New("x509: encrypted PEM data is not a multiple of the block size")
   166  	}
   167  
   168  	data := make([]byte, len(b.Bytes))
   169  	dec := cipher.NewCBCDecrypter(block, iv)
   170  	dec.CryptBlocks(data, b.Bytes)
   171  
   172  	// Blocks are padded using a scheme where the last n bytes of padding are all
   173  	// equal to n. It can pad from 1 to blocksize bytes inclusive. See RFC 1423.
   174  	// For example:
   175  	//	[x y z 2 2]
   176  	//	[x y 7 7 7 7 7 7 7]
   177  	// If we detect a bad padding, we assume it is an invalid password.
   178  	dlen := len(data)
   179  	if dlen == 0 || dlen%ciph.blockSize != 0 {
   180  		return nil, errors.New("x509: invalid padding")
   181  	}
   182  	last := int(data[dlen-1])
   183  	if dlen < last {
   184  		return nil, ErrorIncorrectPassword
   185  	}
   186  	if last == 0 || last > ciph.blockSize {
   187  		return nil, ErrorIncorrectPassword
   188  	}
   189  	for _, val := range data[dlen-last:] {
   190  		if int(val) != last {
   191  			return nil, ErrorIncorrectPassword
   192  		}
   193  	}
   194  	return data[:dlen-last], nil
   195  }
   196  
   197  // EncryptPEMBlock returns a PEM block of the specified type holding the
   198  // given DER encoded data encrypted with the specified algorithm and
   199  // password according to RFC 1423.
   200  func EncryptPEMBlock(rand io.Reader, blockType string, data, password []byte, alg PEMCipher) (*pem.Block, error) {
   201  	ciph := cipherByKey(alg)
   202  	if ciph == nil {
   203  		return nil, errors.New("x509: unknown encryption mode")
   204  	}
   205  	iv := make([]byte, ciph.blockSize)
   206  	if _, err := io.ReadFull(rand, iv); err != nil {
   207  		return nil, errors.New("x509: cannot generate IV: " + err.Error())
   208  	}
   209  	// The salt is the first 8 bytes of the initialization vector,
   210  	// matching the key derivation in DecryptPEMBlock.
   211  	key := ciph.deriveKey(password, iv[:8])
   212  	block, err := ciph.cipherFunc(key)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  	enc := cipher.NewCBCEncrypter(block, iv)
   217  	pad := ciph.blockSize - len(data)%ciph.blockSize
   218  	encrypted := make([]byte, len(data), len(data)+pad)
   219  	// We could save this copy by encrypting all the whole blocks in
   220  	// the data separately, but it doesn't seem worth the additional
   221  	// code.
   222  	copy(encrypted, data)
   223  	// See RFC 1423, Section 1.1.
   224  	for i := 0; i < pad; i++ {
   225  		encrypted = append(encrypted, byte(pad))
   226  	}
   227  	enc.CryptBlocks(encrypted, encrypted)
   228  
   229  	return &pem.Block{
   230  		Type: blockType,
   231  		Headers: map[string]string{
   232  			"Proc-Type": "4,ENCRYPTED",
   233  			"DEK-Info":  ciph.name + "," + hex.EncodeToString(iv),
   234  		},
   235  		Bytes: encrypted,
   236  	}, nil
   237  }
   238  
   239  func cipherByName(name string) *rfc1423Algo {
   240  	for i := range rfc1423Algos {
   241  		alg := &rfc1423Algos[i]
   242  		if alg.name == name {
   243  			return alg
   244  		}
   245  	}
   246  	return nil
   247  }
   248  
   249  func cipherByKey(key PEMCipher) *rfc1423Algo {
   250  	for i := range rfc1423Algos {
   251  		alg := &rfc1423Algos[i]
   252  		if alg.cipher == key {
   253  			return alg
   254  		}
   255  	}
   256  	return nil
   257  }