Go Cryptography

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
func printUsage() {
	fmt.Println("Usage: " + os.Args[0] + " <filepath>")
	fmt.Println("Example: " + os.Args[0] + " document.txt")
}

func checkArgs() string {
	if len(os.Args) < 2 {
		printUsage()
		os.Exit(1)
	}
	return os.Args[1]
}

func main() {
	filename := checkArgs()

	// Get bytes from file
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}

	// Hash the file and output results
	fmt.Printf("Md5: %x\n\n", md5.Sum(data))
	fmt.Printf("Sha1: %x\n\n", sha1.Sum(data))
	fmt.Printf("Sha256: %x\n\n", sha256.Sum256(data))
	fmt.Printf("Sha512: %x\n\n", sha512.Sum512(data))
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
func printUsage() {
	fmt.Println("Usage: " + os.Args[0] + " <filename>")
	fmt.Println("Example: " + os.Args[0] + " diskimage.iso")
}

func checkArgs() string {
	if len(os.Args) < 2 {
		printUsage()
		os.Exit(1)
	}
	return os.Args[1]
}

func main() {
	filename := checkArgs()

	// Open file for reading
	file, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	// Create new hasher, which is a writer interface
	hasher := md5.New()

	// Default buffer size for copying is 32*1024 or 32kb per copy
	// Use io.CopyBuffer() if you want to specify the buffer to use
	// It will write 32kb at a time to the digest/hash until EOF
	// The hasher implements a Write() function making it satisfy
	// the writer interface. The Write() function performs the digest
	// at the time the data is copied/written to it. It digests
	// and processes the hash one chunk at a time as it is received.
	_, err = io.Copy(hasher, file)
	if err != nil {
		log.Fatal(err)
	}

	// Now get the final sum or checksum.
	// We pass nil to the Sum() function because
	// we already copied the bytes via the Copy to the
	// writer interface and don't need to pass any new bytes
	checksum := hasher.Sum(nil)

	fmt.Printf("Md5 checksum: %x\n", checksum)
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
func printUsage() {
	fmt.Println("Usage: " + os.Args[0] + " <password>")
	fmt.Println("Example: " + os.Args[0] + " Password1!")
}
func checkArgs() string {
	if len(os.Args) < 2 {
		printUsage()
		os.Exit(1)
	}
	return os.Args[1]
}

// secretKey should be unique, protected, private,
// and not hard-coded like this. Store in environment var
// or in a secure configuration file.
// This is an arbitrary key that should only be used for example purposes.
var secretKey = "neictr98y85klfgneghre"

// Create a salt string with 32 bytes of crypto/rand data
func generateSalt() string {
	randomBytes := make([]byte, 32)
	_, err := rand.Read(randomBytes)
	if err != nil {
		return ""
	}
	return base64.URLEncoding.EncodeToString(randomBytes)
}

// Hash a password with the salt
func hashPassword(plainText string, salt string) string {
	hash := hmac.New(sha256.New, []byte(secretKey))
	io.WriteString(hash, plainText+salt)
	hashedValue := hash.Sum(nil)
	return hex.EncodeToString(hashedValue)
}

func main() {
	// Get the password from command line argument
	password := checkArgs()
	salt := generateSalt()
	hashedPassword := hashPassword(password, salt)
	fmt.Println("Password: " + password)
	fmt.Println("Salt: " + salt)
	fmt.Println("Hashed password: " + hashedPassword)
}

加密不同于散列,因为它是可逆的并且可以恢复原始消息。有使用密码或共享密钥进行加密和解密的对称加密方法。还有使用公钥和私钥对运行的非对称加密算法。 AES 是对称加密的一个例子,它用于加密 ZIP 文件、PDF 文件或整个文件系统。 RSA 是非对称加密的一个示例,用于 SSL、SSH 密钥和 PGP。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func main() {
	// Generate a random int
	limit := int64(math.MaxInt64) // Highest random number allowed
	randInt, err := rand.Int(rand.Reader, big.NewInt(limit))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Random int value: ", randInt)

	// Alternatively, you could generate the random bytes
	// and turn them into the specific data type needed.
	// binary.Read() will only read enough bytes to fill the data type
	var number uint32
	err = binary.Read(rand.Reader, binary.BigEndian, &number)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Random uint32 value: ", number)

	// Or just generate a random byte slice
	numBytes := 4
	randomBytes := make([]byte, numBytes)
	rand.Read(randomBytes)
	fmt.Println("Random byte values: ", randomBytes)
}

对称加密是指使用相同的密钥或密码来加密和解密数据。高级加密标准,也称为 AES 或 Rijndael,是 NIST 在 2001 年制定的一种对称加密算法的标准。数据加密标准,或称 DES,是另一种对称加密算法,比 AES 更老,更不安全。除非有特定的要求或规范,否则不应该在 AES 之上使用它。Go 标准库包括 AES 和 DES 包。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
func printUsage() {
	fmt.Printf(os.Args[0] + `

Encrypt or decrypt a file using AES with a 256-bit key file.
This program can also generate 256-bit keys.

Usage:
  ` + os.Args[0] + ` [-h|--help]
  ` + os.Args[0] + ` [-g|--genkey]
  ` + os.Args[0] + ` <keyFile> <file> [-d|--decrypt]

Examples:
  # Generate a 32-byte (256-bit) key
  ` + os.Args[0] + ` --genkey

  # Encrypt with secret key. Output to STDOUT
  ` + os.Args[0] + ` --genkey > secret.key

  # Encrypt message using secret key. Output to ciphertext.dat
  ` + os.Args[0] + ` secret.key message.txt > ciphertext.dat

  # Decrypt message using secret key. Output to STDOUT
  ` + os.Args[0] + ` secret.key ciphertext.dat -d

  # Decrypt message using secret key. Output to message.txt
  ` + os.Args[0] + ` secret.key ciphertext.dat -d > cleartext.txt
`)
}

// Check command-line arguments.
// If the help or generate key functions are chosen
// they are run and then the program exits
// otherwise it returns  keyFile, file, decryptFlag.
func checkArgs() (string, string, bool) {
	if len(os.Args) < 2 || len(os.Args) > 4 {
		printUsage()
		os.Exit(1)
	}

	// One arg provided
	if len(os.Args) == 2 {
		// Only -h, --help and --genkey are valid one-argument uses
		if os.Args[1] == "-h" || os.Args[1] == "--help" {
			printUsage() // Print help text
			os.Exit(0)   // Exit gracefully no error
		}
		if os.Args[1] == "-g" || os.Args[1] == "--genkey" {
			// Generate a key and print to STDOUT
			// User should redirect output to a file if needed
			key := generateKey()
			fmt.Printf(string(key[:])) // No newline
			os.Exit(0)                 // Exit gracefully

		}
	}

	// The only use options left is
	// encrypt <keyFile> <file> [-d|--decrypt]
	// If there are only 2 args provided, they must be the
	// keyFile and file without a decrypt flag.
	if len(os.Args) == 3 {
		return os.Args[1], os.Args[2], false // keyFile, file, decryptFlag
	}
	// If 3 args are provided, check that the last one is -d or --decrypt
	if len(os.Args) == 4 {
		if os.Args[3] != "-d" && os.Args[3] != "--decrypt" {
			fmt.Println("Error: Unknown usage.")
			printUsage()
			os.Exit(1) // Exit with error code
		}
		return os.Args[1], os.Args[2], true
	}

	return "", "", false // Default blank return
}

func generateKey() []byte {
	randomBytes := make([]byte, 32) // 32 bytes, 256 bit
	numBytesRead, err := rand.Read(randomBytes)
	if err != nil {
		log.Fatal("Error generating random key.", err)
	}
	if numBytesRead != 32 {
		log.Fatal("Error generating 32 random bytes for key.")
	}
	return randomBytes
}

// AES encryption
func encrypt(key, message []byte) ([]byte, error) {
	// Initialize block cipher
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	// Create the byte slice that will hold encrypted message
	cipherText := make([]byte, aes.BlockSize+len(message))

	// Generate the Initialization Vector (IV) nonce
	// which is stored at the beginning of the byte slice
	// The IV is the same length as the AES blocksize
	iv := cipherText[:aes.BlockSize]
	_, err = io.ReadFull(rand.Reader, iv)
	if err != nil {
		return nil, err
	}

	// Choose the block cipher mode of operation
	// Using the cipher feedback (CFB) mode here.
	// CBCEncrypter also available.
	cfb := cipher.NewCFBEncrypter(block, iv)
	// Generate the encrypted message and store it
	// in the remaining bytes after the IV nonce
	cfb.XORKeyStream(cipherText[aes.BlockSize:], message)

	return cipherText, nil
}

// AES decryption
func decrypt(key, cipherText []byte) ([]byte, error) {
	// Initialize block cipher
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	// Separate the IV nonce from the encrypted message bytes
	iv := cipherText[:aes.BlockSize]
	cipherText = cipherText[aes.BlockSize:]

	// Decrypt the message using the CFB block mode
	cfb := cipher.NewCFBDecrypter(block, iv)
	cfb.XORKeyStream(cipherText, cipherText)

	return cipherText, nil
}

func main() {
	// if generate key flag, just output a key to stdout and exit
	keyFile, file, decryptFlag := checkArgs()

	// Load key from file
	keyFileData, err := ioutil.ReadFile(keyFile)
	if err != nil {
		log.Fatal("Unable to read key file contents.", err)
	}

	// Load file to be encrypted or decrypted
	fileData, err := ioutil.ReadFile(file)
	if err != nil {
		log.Fatal("Unable to read key file contents.", err)
	}

	// Perform encryption unless the decryptFlag was provided
	// Outputs to STDOUT. User can redirect output to file.
	if decryptFlag {
		message, err := decrypt(keyFileData, fileData)
		if err != nil {
			log.Fatal("Error decrypting. ", err)
		}
		fmt.Printf("%s", message)
	} else {
		cipherText, err := encrypt(keyFileData, fileData)
		if err != nil {
			log.Fatal("Error encrypting. ", err)
		}
		fmt.Printf("%s", cipherText)
	}
}

非对称性是指每一方都有两把钥匙。每一方都需要一对公钥和私钥。非对称加密算法包括 RSA、DSA 和 ECDSA。Go 标准库中有 RSA、DSA 和 ECDSA 的包。一些使用非对称加密的应用程序包括安全外壳(SSH)、安全套接字层(SSL)和漂亮的隐私(PGP)。SSL 是最初由 Netscape 开发的安全套接字层,第二版于 1995 年公开发布。它用于加密服务器和客户端之间的通信,提供保密性、完整性和认证。TLS,即传输层安全,是 SSL 的新版本,1.2 版在 2008 年被定义为 RFC 5246。TLS 的 Go 包并没有完全实现该规范,但它实现了主要部分。阅读更多关于 Go 的 crypto/tls 包的信息:http s://golang.org/pkg/crypto/tls/。你只能对小于密钥大小的东西进行加密,而密钥大小往往是 2048 位。由于这种大小的限制,非对称 RSA 加密对于加密整个文件来说并不实用,因为这些文件很容易超过 2048 比特或 256 字节。另一方面,对称加密,如 AES 可以加密大型文件,但它需要双方共享密钥。TLS/SSL 使用非对称和对称加密的组合。最初的连接和握手是用双方的公钥和私钥进行非对称加密的。一旦建立了连接,就会产生和共享一个共享密钥。一旦双方都知道共享密钥,非对称加密就会被放弃,其余的通信则使用对称加密,如使用共享密钥的 AES。

使用 OpenSSL 生成私钥和公钥

1
2
3
4
# Generate the private key
openssl genrsa -out priv.pem 2048
# Extract the public key from the private key
openssl rsa -in priv.pem -pubout -out public.pem
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
func printUsage() {
	fmt.Printf(os.Args[0] + `

Generate a private and public RSA keypair and save as PEM files.
If no key size is provided, a default of 2048 is used.

Usage:
  ` + os.Args[0] + ` <private_key_filename> <public_key_filename> [keysize]

Examples:
  # Store generated private and public key in privkey.pem and pubkey.pem
  ` + os.Args[0] + ` priv.pem pub.pem
  ` + os.Args[0] + ` priv.pem pub.pem 4096`)
}

func checkArgs() (string, string, int) {
	// Too many or too few arguments
	if len(os.Args) < 3 || len(os.Args) > 4 {
		printUsage()
		os.Exit(1)
	}

	defaultKeySize := 2048

	// If there are 2 args provided, privkey and pubkey filenames
	if len(os.Args) == 3 {
		return os.Args[1], os.Args[2], defaultKeySize
	}

	// If 3 args provided, privkey, pubkey, keysize
	if len(os.Args) == 4 {
		keySize, err := strconv.Atoi(os.Args[3])
		if err != nil {
			printUsage()
			fmt.Println("Keysize not a valid number. Try 1024 or 2048.")
			os.Exit(1)
		}
		return os.Args[1], os.Args[2], keySize
	}

	return "", "", 0 // Default blank return catch-all
}

// Encode the private key as a PEM file
// PEM is a base-64 encoding of the key
func getPrivatePemFromKey(privateKey *rsa.PrivateKey) *pem.Block {
	encodedPrivateKey := x509.MarshalPKCS1PrivateKey(privateKey)
	var privatePem = &pem.Block {
		Type: "RSA PRIVATE KEY",
		Bytes: encodedPrivateKey,
	}
	return privatePem
}

// Encode the public key as a PEM file
func generatePublicPemFromKey(publicKey rsa.PublicKey) *pem.Block {
	encodedPubKey, err := x509.MarshalPKIXPublicKey(&publicKey)
	if err != nil {
		log.Fatal("Error marshaling PKIX pubkey. ", err)
	}

	// Create a public PEM structure with the data
	var publicPem = &pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: encodedPubKey,
	}
	return publicPem
}

func savePemToFile(pemBlock *pem.Block, filename string) {
	// Save public pem to file
	publicPemOutputFile, err := os.Create(filename)
	if err != nil {
		log.Fatal("Error opening pubkey output file. ", err)
	}
	defer publicPemOutputFile.Close()


	err = pem.Encode(publicPemOutputFile, pemBlock)
	if err != nil {
		log.Fatal("Error encoding public PEM. ", err)
	}
}

// Generate a public and private RSA key in PEM format
func main() {
	privatePemFilename, publicPemFilename, keySize := checkArgs()

	// Generate private key
	privateKey, err := rsa.GenerateKey(rand.Reader, keySize)
	if err != nil {
		log.Fatal("Error generating private key. ", err)
	}

	// Encode keys to PEM format
	privatePem := getPrivatePemFromKey(privateKey)
	publicPem := generatePublicPemFromKey(privateKey.PublicKey)

	// Save the PEM output to files
	savePemToFile(privatePem, privatePemFilename)
	savePemToFile(publicPem, publicPemFilename)

	// Print the public key to STDOUT for convenience
	fmt.Printf("%s", pem.EncodeToMemory(publicPem))
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
func printUsage() {
	fmt.Println(os.Args[0] + `

Cryptographically sign a message using a private key.
Private key should be a PEM encoded RSA key.
Signature is generated using SHA256 hash.
Output signature is stored in filename provided.

Usage:
  ` + os.Args[0] + ` <privateKeyFilename> <messageFilename> <signatureFilename>

Example:
  # Use priv.pem to encrypt msg.txt and output to sig.txt.256
  ` + os.Args[0] + ` priv.pem msg.txt sig.txt.256
`)
}

// Get arguments from command line
func checkArgs() (string, string, string) {
	// Need exactly 3 arguments provided
	if len(os.Args) != 4 {
		printUsage()
		os.Exit(1)
	}

	// Private key file name and message file name
	return os.Args[1], os.Args[2], os.Args[3]
}

// Cryptographically sign a message= creating a digital signature
// of the original message. Uses SHA-256 hashing.
func signMessage(privateKey *rsa.PrivateKey, message []byte) []byte {
	hashed := sha256.Sum256(message)

	signature, err := rsa.SignPKCS1v15(
		rand.Reader,
		privateKey,
		crypto.SHA256,
		hashed[:],
	)
	if err != nil {
		log.Fatal("Error signing message. ", err)
	}

	return signature
}

// Load the message that will be signed from file
func loadMessageFromFile(messageFilename string) []byte {
	fileData, err := ioutil.ReadFile(messageFilename)
	if err != nil {
		log.Fatal(err)
	}
	return fileData
}

// Load the RSA private key from a PEM encoded file
func loadPrivateKeyFromPemFile(privateKeyFilename string) *rsa.PrivateKey {
	// Quick load file to memory
	fileData, err := ioutil.ReadFile(privateKeyFilename)
	if err != nil {
		log.Fatal(err)
	}

	// Get the block data from the PEM encoded file
	block, _ := pem.Decode(fileData)
	if block == nil || block.Type != "RSA PRIVATE KEY" {
		log.Fatal("Unable to load a valid private key.")
	}

	// Parse the bytes and put it in to a proper privateKey struct
	privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		log.Fatal("Error loading private key.", err)
	}

	return privateKey
}

// Save data to file
func writeToFile(filename string, data []byte) error {
	// Open a new file for writing only
	file, err := os.OpenFile(
		filename,
		os.O_WRONLY|os.O_TRUNC|os.O_CREATE,
		0666,
	)
	if err != nil {
		return err
	}
	defer file.Close()

	// Write bytes to file
	_, err = file.Write(data)
	if err != nil {
		return err
	}

	return nil
}

// Sign a message using a private RSA key
func main() {
	// Get arguments from command line
	privateKeyFilename, messageFilename, sigFilename := checkArgs()

	// Load message and private key files from disk
	message := loadMessageFromFile(messageFilename)
	privateKey := loadPrivateKeyFromPemFile(privateKeyFilename)

	// Cryptographically sign the message
	signature := signMessage(privateKey, message)

	// Output to file
	writeToFile(sigFilename, signature)
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
func printUsage() {
	fmt.Println(os.Args[0] + `

Verify an RSA signature of a message using SHA-256 hashing.
Public key is expected to be a PEM file.

Usage:

  ` + os.Args[0] + ` <publicKeyFilename> <signatureFilename> <messageFilename>

Example:
  ` + os.Args[0] + ` pubkey.pem signature.txt message.txt
`)
}

// Get arguments from command line
func checkArgs() (string, string, string) {
	// Expect 3 arguments: pubkey, signature, message file names
	if len(os.Args) != 4 {
		printUsage()
		os.Exit(1)
	}

	return os.Args[1], os.Args[2], os.Args[3]
}

// Returns bool whether signature was verified
func verifySignature(
	signature []byte,
	message []byte,
	publicKey *rsa.PublicKey) bool {

	hashedMessage := sha256.Sum256(message)

	err := rsa.VerifyPKCS1v15(
		publicKey,
		crypto.SHA256,
		hashedMessage[:],
		signature,
	)

	if err != nil {
		log.Println(err)
		return false
	}
	return true // If no error, match.
}

// Load file to memory
func loadFile(filename string) []byte {
	fileData, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}
	return fileData
}

// Load a public RSA key from a PEM encoded file
func loadPublicKeyFromPemFile(publicKeyFilename string) *rsa.PublicKey {
	// Quick load file to memory
	fileData, err := ioutil.ReadFile(publicKeyFilename)
	if err != nil {
		log.Fatal(err)
	}

	// Get the block data from the PEM encoded file
	block, _ := pem.Decode(fileData)
	if block == nil || block.Type != "PUBLIC KEY" {
		log.Fatal("Unable to load valid public key. ")
	}

	// Parse the bytes and store in a public key format
	publicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		log.Fatal("Error loading public key. ", err)
	}

	return publicKey.(*rsa.PublicKey) // Cast interface to PublicKey
}

// Verify a cryptographic signature using RSA PKCS#1 v1.5 with SHA-256
// and a PEM encoded PKIX public key.
func main() {
	// Parse command line arguments
	publicKeyFilename, signatureFilename, messageFilename :=
		checkArgs()

	// Load all the files from disk
	publicKey := loadPublicKeyFromPemFile(publicKeyFilename)
	signature := loadFile(signatureFilename)
	message := loadFile(messageFilename)

	// Verify signature
	valid := verifySignature(signature, message, publicKey)

	if valid {
		fmt.Println("Signature verified.")
	} else {
		fmt.Println("Signature could not be verified.")
	}
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
func printUsage() {
	fmt.Println(os.Args[0] + ` - Generate a self signed TLS certificate

Usage:
  ` + os.Args[0] + ` <privateKeyFilename> <certOutputFilename> [-ca|--cert-authority]

Example:
  ` + os.Args[0] + ` priv.pem cert.pem
  ` + os.Args[0] + ` priv.pem cacert.pem -ca
`)
}

func checkArgs() (string, string, bool) {
	if len(os.Args) < 3 || len(os.Args) > 4 {
		printUsage()
		os.Exit(1)
	}

	// See if the last cert authority option was passed
	isCA := false // Default
	if len(os.Args) == 4 {
		if os.Args[3] == "-ca" || os.Args[3] == "--cert-authority" {
			isCA = true
		}
	}

	// Private key filename, cert output filename, is cert authority
	return os.Args[1], os.Args[2], isCA
}

func setupCertificateTemplate(isCA bool) x509.Certificate {
	// Set valid time frame to start now and end one year from now
	notBefore := time.Now()
	notAfter := notBefore.Add(time.Hour * 24 * 365) // 1 year/365 days

	// Generate secure random serial number
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	randomNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		log.Fatal("Error generating random serial number. ", err)
	}

	nameInfo := pkix.Name{
		Organization: []string{"My Organization"},
		CommonName: "localhost",
		OrganizationalUnit: []string{"My Business Unit"},
		Country:            []string{"US"}, // 2-character ISO code
		Province:           []string{"Texas"}, // State
		Locality:           []string{"Houston"}, // City
	}

	// Create the certificate template
	certTemplate := x509.Certificate{
		SerialNumber: randomNumber,
		Subject: nameInfo,
		EmailAddresses: []string{"test@localhost"},
		NotBefore: notBefore,
		NotAfter: notAfter,
		KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		// For ExtKeyUsage, default to any, but can specify to use
		// only as server or client authentication, code signing, etc
		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageAny },
		BasicConstraintsValid: true,
		IsCA: false,
	}

	// To create a certificate authority that can sign cert signing requests, set these
	if isCA {
	    certTemplate.IsCA = true
	    certTemplate.KeyUsage = certTemplate.KeyUsage | x509.KeyUsageCertSign
	}

	// Add any IP addresses and hostnames covered by this cert
	// This example only covers localhost
	certTemplate.IPAddresses = []net.IP{net.ParseIP("127.0.0.1")}
	certTemplate.DNSNames = []string{"localhost", "localhost.local"}

	return certTemplate
}

// Load the RSA private key from a PEM encoded file
func loadPrivateKeyFromPemFile(privateKeyFilename string) *rsa.PrivateKey {
	// Quick load file to memory
	fileData, err := ioutil.ReadFile(privateKeyFilename)
	if err != nil {
		log.Fatal("Error loading private key file. ", err)
	}

	// Get the block data from the PEM encoded file
	block, _ := pem.Decode(fileData)
	if block == nil || block.Type != "RSA PRIVATE KEY" {
		log.Fatal("Unable to load a valid private key.")
	}

	// Parse the bytes and put it in to a proper privateKey struct
	privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		log.Fatal("Error loading private key. ", err)
	}

	return privateKey
}

// Save the certificate as a PEM encoded file
func writeCertToPemFile(outputFilename string, derBytes []byte ) {
	// Create a PEM from the certificate
	certPem := &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}

	// Open file for writing
	certOutfile, err := os.Create(outputFilename)
	if err != nil {
		log.Fatal("Unable to open certificate output file. ", err)
	}
	pem.Encode(certOutfile, certPem)
	certOutfile.Close()
}

// Create a self-signed TLS/SSL certificate for localhost with an RSA private key
func main() {
	privPemFilename, certOutputFilename, isCA := checkArgs()

	// Private key of signer - self signed means signer==signee
	privKey := loadPrivateKeyFromPemFile(privPemFilename)

	// Public key of signee. Self signing means we are the signer and the signee
	// so we can just pull our public key from our private key
	pubKey := privKey.PublicKey

	// Set up all the certificate info
	certTemplate := setupCertificateTemplate(isCA)

	// Create (and sign with the priv key) the certificate
	certificate, err := x509.CreateCertificate(
		rand.Reader,
		&certTemplate,
		&certTemplate,
		&pubKey,
		privKey,
	)
	if err != nil {
		log.Fatal("Failed to create certificate. ", err)
	}

	// Format the certificate as a PEM and write to file
	writeCertToPemFile(certOutputFilename, certificate)
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
func printUsage() {
	fmt.Println(os.Args[0] + ` - Create a certificate signing request with a private key

Private key is expected in PEM format. Certificate valid for localhost only.
Certificate signing request is created using the SHA-256 hash.

Usage:
  ` + os.Args[0] + ` <privateKeyFilename> <csrOutputFilename>

Example:
  ` + os.Args[0] + ` priv.pem csr.pem
`)
}

func checkArgs() (string, string) {
	if len(os.Args) != 3 {
		printUsage()
		os.Exit(1)
	}

	// Private key filename, cert signing request output filename
	return os.Args[1], os.Args[2]
}

// Load the RSA private key from a PEM encoded file
func loadPrivateKeyFromPemFile(privateKeyFilename string) *rsa.PrivateKey {
	// Quick load file to memory
	fileData, err := ioutil.ReadFile(privateKeyFilename)
	if err != nil {
		log.Fatal("Error loading private key file. ", err)
	}

	// Get the block data from the PEM encoded file
	block, _ := pem.Decode(fileData)
	if block == nil || block.Type != "RSA PRIVATE KEY" {
		log.Fatal("Unable to load a valid private key.")
	}

	// Parse the bytes and put it in to a proper privateKey struct
	privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		log.Fatal("Error loading private key.", err)
	}

	return privateKey
}

// Create a CSR PEM and save to file
func saveCSRToPemFile(csr []byte, filename string) {
	csrPem := &pem.Block{
		Type:  "CERTIFICATE REQUEST",
		Bytes: csr,
	}
	csrOutfile, err := os.Create(filename)
	if err != nil {
		log.Fatal("Error opening "+filename+" for saving. ", err)
	}
	pem.Encode(csrOutfile, csrPem)
}

// Create a certificate signing request with a private key valid for localhost
func main() {
	// Load parameters
	privKeyFilename, csrOutFilename := checkArgs()
	privKey := loadPrivateKeyFromPemFile(privKeyFilename)

	// Prepare information about organization the cert will belong to
	nameInfo := pkix.Name{
		Organization:       []string{"My Organization Name"},
		CommonName:         "localhost",
		OrganizationalUnit: []string{"Business Unit Name"},
		Country:            []string{"US"}, // 2-character ISO code
		Province:           []string{"Texas"},
		Locality:           []string{"Houston"}, // City
	}

	// Prepare CSR template
	csrTemplate := x509.CertificateRequest{
		Version:            2, // Version 3, zero-indexed values
		SignatureAlgorithm: x509.SHA256WithRSA,
		PublicKeyAlgorithm: x509.RSA,
		PublicKey:          privKey.PublicKey,
		Subject:            nameInfo,

		// Subject Alternate Name values.
		DNSNames:       []string{"Business Unit Name"},
		EmailAddresses: []string{"test@localhost"},
		IPAddresses:    []net.IP{},
	}

	// Create the CSR based off the template
	csr, err := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, privKey)
	if err != nil {
		log.Fatal("Error creating certificate signing request. ", err)
	}
	saveCSRToPemFile(csr, csrOutFilename)
}

在自签的例子中,模板和父级证书是同一个对象。要签署一个证书请求,创建一个新的证书对象,并用签署请求中的信息填充字段。将新的证书作为模板,并将签名人的证书作为父证书。pub 参数是收件人的公钥,priv 参数是签名人的私钥。签名者是证书颁发机构,而被签名者是请求者。

The X509.CreateCertificate() parameters are as follows:

rand: This is the cryptographically secure pseudorandom number generator template: This is the certificate template populated with info from CSR

parent: This is the certificate of the signer

pub: This is the public key of the signee

priv: This is the private key of the signer 等价于

1
2
3
4
5
6
7
# Create signed certificate using
# the CSR, CA certificate, and private key
openssl x509 -req -in csr.pem -CA cacert.pem \
-CAkey capriv.pem -CAcreateserial \
-out cert.pem -sha256
# Print info about cert
openssl x509 -in cert.pem -text -noout

你可以像普通的套接字连接一样设置一个监听器,但是要进行加密。只要调用 TLS Listen()函数,并向它提供你的证书和私钥。用前面的例子生成的证书和密钥可以使用。下面的程序将创建一个 TLS 服务器并回传任何收到的数据,然后关闭连接。该服务器将不要求或验证客户的证书,但如果你想用证书来验证客户的身份,就把这样做的代码注释出来供参考。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
func printUsage() {
	fmt.Println(os.Args[0] + ` - Start a TLS echo server

Server will echo one message received back to client.
Provide a certificate and private key file in PEM format.
Host string in the format: hostname:port

Usage:
  ` + os.Args[0] + ` <certFilename> <privateKeyFilename> <hostString>

Example:
  ` + os.Args[0] + ` cert.pem priv.pem localhost:9999
`)
}

func checkArgs() (string, string, string) {
	if len(os.Args) != 4 {
		printUsage()
		os.Exit(1)
	}

	return os.Args[1], os.Args[2], os.Args[3]
}

// Create a TLS listener and echo back data received by clients.
func main() {
	certFilename, privKeyFilename, hostString := checkArgs()

	// Load the certificate and private key
	serverCert, err := tls.LoadX509KeyPair(certFilename, privKeyFilename)
	if err != nil {
		log.Fatal("Error loading certificate and private key. ", err)
	}

	// Set up certificates, host/ip, and port
	config := &tls.Config{
		// Specify server certificate
		Certificates: []tls.Certificate{serverCert},

		// By default no client certificate is required.
		// To require and validate client certificates, specify the
		// ClientAuthType to be one of:
		//     NoClientCert, RequestClientCert, RequireAnyClientCert,
		//     VerifyClientCertIfGiven, RequireAndVerifyClientCert)

		// ClientAuth: tls.RequireAndVerifyClientCert

		// Define the list of certificates you will accept as
		// trusted certificate authorities with ClientCAs.

		// ClientCAs: *x509.CertPool
	}

	// Create the TLS socket listener
	listener, err := tls.Listen("tcp", hostString, config)
	if err != nil {
		log.Fatal("Error starting TLS listener. ", err)
	}
	defer listener.Close()

	// Listen forever for connections
	for {
		clientConnection, err := listener.Accept()
		if err != nil {
			log.Println("Error accepting client connection. ", err)
			continue
		}
		// Launch a goroutine(thread) to handle each connection
		go handleConnection(clientConnection)
	}
}

// Function that gets launched in a goroutine to handle client connection
func handleConnection(clientConnection net.Conn) {
	defer clientConnection.Close()
	socketReader := bufio.NewReader(clientConnection)
	for {
		// Read a message from the client
		message, err := socketReader.ReadString('\n')
		if err != nil {
			log.Println("Error reading from client socket. ", err)
			return
		}
		fmt.Println(message)

		// Echo back the data to the client.
		numBytesWritten, err := clientConnection.Write([]byte(message))
		if err != nil {
			log.Println("Error writing data to client socket. ", err)
			return
		}
		fmt.Printf("Wrote %d bytes back to client.\n", numBytesWritten)
	}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
func printUsage() {
	fmt.Println(os.Args[0] + ` - Send and receive a message to a TLS server

Usage:
  ` + os.Args[0] + ` <hostString>

Example:
  ` + os.Args[0] + ` localhost:9999
`)
}

func checkArgs() string {
	if len(os.Args) != 2 {
		printUsage()
		os.Exit(1)
	}

	// Host string e.g. localhost:9999
	return os.Args[1]
}

// Simple TLS client that sends a message and receives a message
func main() {
	hostString := checkArgs()
	messageToSend := "Hello?\n"

	// Configure TLS settings
	tlsConfig := &tls.Config{
		InsecureSkipVerify: true, // Required to accept self-signed certs

		// Provide your client certificate if necessary
		// Certificates: []Certificate

		// ServerName is used to verify the hostname (unless you are skipping verification)
		// It is also included in the handshake in case the server uses virtual hosts
		// Can also just be an IP address instead of a hostname.
		// ServerName: string,

		// RootCAs that you are willing to accept
		// If RootCAs is nil, the host's default root CAs are used
		// RootCAs: *x509.CertPool
	}

	// Set up dialer and call the server
	connection, err := tls.Dial("tcp", hostString, tlsConfig)
	if err != nil {
		log.Fatal("Error dialing server. ", err)
	}
	defer connection.Close()

	// Write data to socket
	numBytesWritten, err := connection.Write([]byte(messageToSend))
	if err != nil {
		log.Println("Error writing to socket. ", err)
		os.Exit(1)
	}
	fmt.Printf("Wrote %d bytes to the socket.\n", numBytesWritten)

	// Read data from socket and print to STDOUT
	buffer := make([]byte, 100)
	numBytesRead, err := connection.Read(buffer)
	if err != nil {
		log.Println("Error reading from socket. ", err)
		os.Exit(1)
	}
	fmt.Printf("Read %d bytes to the socket.\n", numBytesRead)
	fmt.Printf("Message received:\n%s\n", buffer)
}

PGP 代表 Pretty Good Privacy,而 OpenPGP 是标准的 RFC 4880。PGP 是一个用于加密文本、文件、目录和磁盘的便捷套件。所有原则都与上一节中讨论的 SSL 和 TLS 密钥/证书相同。加密、签名和验证都是一样的。 Go 提供了一个 OpenPGP 包。https://godoc.org/golang.org/x/crypto/openpgp

Off The Record 或 OTR 消息传递是一种端到端的加密形式,供用户在任何消息媒介上加密他们的通信。它很方便,因为你可以在任何协议上实现一个加密层,即使该协议本身是不加密的。例如,OTR 消息传递可以在 XMPP、IRC 和许多其他聊天协议上使用。许多聊天客户端,如 Pidgin、Adium 和 Xabber,都有对 OTR 的支持,无论是原生的还是通过插件。Go 提供了一个实现 OTR 消息传递的包。https://godoc.org/golang.org/x/crypto/otr/

相关内容