github.com/webx-top/com@v1.2.12/password.go (about) 1 /* 2 3 Copyright 2016 Wenhui Shen <www.webx.top> 4 5 Licensed under the Apache License, Version 2.0 (the "License"); 6 you may not use this file except in compliance with the License. 7 You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11 Unless required by applicable law or agreed to in writing, software 12 distributed under the License is distributed on an "AS IS" BASIS, 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 See the License for the specific language governing permissions and 15 limitations under the License. 16 17 */ 18 19 package com 20 21 import ( 22 "crypto/hmac" 23 "crypto/sha1" 24 "hash" 25 ) 26 27 // Hash 生成哈希值 28 func Hash(str string) string { 29 return Sha256(str) 30 } 31 32 // Salt 盐值加密(生成64个字符) 33 func Salt() string { 34 return Hash(RandStr(64)) 35 } 36 37 // MakePassword 创建密码(生成64个字符) 38 // 可以指定positions用来在hash处理后的密码的不同位置插入salt片段(数量取决于positions的数量),然后再次hash 39 func MakePassword(password string, salt string, positions ...uint) string { 40 length := len(positions) 41 if length < 1 { 42 return Hash(salt + password) 43 } 44 saltLength := len(salt) 45 if saltLength < length { 46 return Hash(salt + password) 47 } 48 saltChars := saltLength / length 49 hashedPassword := Hash(password) 50 maxIndex := len(hashedPassword) - 1 51 saltMaxIndex := saltLength - 1 52 var result string 53 var lastPos int 54 for k, pos := range positions { 55 end := int(pos) 56 start := lastPos 57 if start > end { 58 start, end = end, start 59 } 60 if start > maxIndex { 61 continue 62 } 63 lastPos = end 64 saltStart := k * saltChars 65 saltEnd := saltStart + saltChars 66 if end > maxIndex { 67 result += hashedPassword[start:] + salt[saltStart:saltEnd] 68 continue 69 } 70 result += hashedPassword[start:end] + salt[saltStart:saltEnd] 71 if k == length-1 { 72 if end <= maxIndex { 73 result += hashedPassword[end:] 74 } 75 if saltEnd <= saltMaxIndex { 76 result += salt[saltEnd:] 77 } 78 } 79 } 80 return Hash(result) 81 } 82 83 // CheckPassword 检查密码(密码原文,数据库中保存的哈希过后的密码,数据库中保存的盐值) 84 func CheckPassword(rawPassword string, hashedPassword string, salt string, positions ...uint) bool { 85 return MakePassword(rawPassword, salt, positions...) == hashedPassword 86 } 87 88 // PBKDF2Key derives a key from the password, salt and iteration count, returning a 89 // []byte of length keylen that can be used as cryptographic key. The key is 90 // derived based on the method described as PBKDF2 with the HMAC variant using 91 // the supplied hash function. 92 // 93 // For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you 94 // can get a derived key for e.g. AES-256 (which needs a 32-byte key) by 95 // doing: 96 // 97 // dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New) 98 // 99 // Remember to get a good random salt. At least 8 bytes is recommended by the 100 // RFC. 101 // 102 // Using a higher iteration count will increase the cost of an exhaustive 103 // search but will also make derivation proportionally slower. 104 func PBKDF2Key(password, salt []byte, iter, keyLen int, hFunc ...func() hash.Hash) string { 105 var h func() hash.Hash 106 if len(hFunc) > 0 { 107 h = hFunc[0] 108 } 109 if h == nil { 110 h = sha1.New 111 } 112 prf := hmac.New(h, password) 113 hashLen := prf.Size() 114 numBlocks := (keyLen + hashLen - 1) / hashLen 115 116 var buf [4]byte 117 dk := make([]byte, 0, numBlocks*hashLen) 118 U := make([]byte, hashLen) 119 for block := 1; block <= numBlocks; block++ { 120 // N.B.: || means concatenation, ^ means XOR 121 // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter 122 // U_1 = PRF(password, salt || uint(i)) 123 prf.Reset() 124 prf.Write(salt) 125 buf[0] = byte(block >> 24) 126 buf[1] = byte(block >> 16) 127 buf[2] = byte(block >> 8) 128 buf[3] = byte(block) 129 prf.Write(buf[:4]) 130 dk = prf.Sum(dk) 131 T := dk[len(dk)-hashLen:] 132 copy(U, T) 133 134 // U_n = PRF(password, U_(n-1)) 135 for n := 2; n <= iter; n++ { 136 prf.Reset() 137 prf.Write(U) 138 U = U[:0] 139 U = prf.Sum(U) 140 for x := range U { 141 T[x] ^= U[x] 142 } 143 } 144 } 145 return Base64Encode(string(dk[:keyLen])) 146 }