说明
本篇相当于是一个hello world,是一个系列的开篇,本系列后面将会包含但不限于
- web端rsa加密登录,js混淆
- 后端包含支持https,rsa解密
- Android端将会包含apk混淆,加固,证书校验(只信任自己的证书)
- openssl证书生成等
前端代码
前端是Vue,采用jsencrypt进行加密,请求的时候会有跨域问题
<template>
<div class="center">
<el-row>
<el-col :span="24">
<el-input prefix-icon="el-icon-user" placeholder="请输入用户名" v-model="username" clearable></el-input>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-input prefix-icon="el-icon-lock" placeholder="请输入密码" v-model="password" show-password></el-input>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-checkbox v-model="remember_me">记住登录</el-checkbox>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-button class="custom-btn" type="primary" plain @click="login()">登录</el-button>
</el-col>
</el-row>
<el-row>
<el-col :span="24" />
<el-input type="textarea" :rows="2" placeholder="请输入内容" v-model="textarea"></el-input>
</el-row>
</div>
</template>
<script>
import axios from "axios";
import JSEncrypt from "jsencrypt";
export default {
name: "Login",
data() {
return {
username: "",
password: "",
remember_me: false,
textarea: "",
publicKey: "",
};
},
created() {
this.getPubKey();
},
methods: {
getPubKey() {
const url = "http://127.0.0.1:8081/getPubKey";
axios
.get(url)
.then((response) => {
console.log("response data is:" + response.data.message);
this.publicKey = response.data.message;
//this.textarea = this.publicKey;
})
.catch((response) => {
console.log("请求失败..." + response);
});
},
login() {
let encryptPd = this.ras_encrypt();
console.log("password after encrypt is:" + encryptPd);
const url = "http://127.0.0.1:8081/login";
let data = {
"encryptPwd":encryptPd
}
axios.post(url, data).then((res) => {
console.log("res=>", res);
});
},
ras_encrypt() {
let encryptStr = new JSEncrypt();
encryptStr.setPublicKey(this.publicKey); // 设置 加密公钥
let data = encryptStr.encrypt(this.password); // 进行加密
return data;
},
},
};
</script>
<style>
.center {
margin-left: 100px;
margin-top: 50px;
height: 200px;
width: 300px;
}
.el-row {
margin-bottom: 10px;
}
.el-col {
border-radius: 4px;
}
.custom-btn {
width: 100%;
}
</style>
后端代码
后端采用Golang的Gin并允许跨域
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"strings"
)
var (
privateKeyStr string
publicKeyStr string
)
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method :=c.Request.Method
origin := c.Request.Header.Get("Origin") //请求头部
var headerKeys []string // 声明请求头keys
for k, _ := range c.Request.Header {
headerKeys = append(headerKeys, k)
}
headerStr := strings.Join(headerKeys, ", ")
if headerStr != "" {
headerStr = fmt.Sprintf("access-control-allow-origin, access-control-allow-headers, %s", headerStr)
} else {
headerStr = "access-control-allow-origin, access-control-allow-headers"
}
if origin != "" {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Origin", "*") // 这是允许访问所有域
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE") //服务器支持的所有跨域请求的方法,为了避免浏览次请求的多次'预检'请求
// header的类型
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma")
// 允许跨域设置 可以返回其他子段
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") // 跨域关键设置 让浏览器可以解析
c.Header("Access-Control-Max-Age", "172800") // 缓存请求信息 单位为秒
c.Header("Access-Control-Allow-Credentials", "false") // 跨域请求是否需要带cookie信息 默认设置为true
c.Set("content-type", "application/json") // 设置返回格式是json
}
if method == "OPTIONS" {
c.JSON(http.StatusOK, "Options Request!")
}
c.Next()// 处理请求
}
}
func StartServer(){
r := gin.Default()
r.Use(Cors())
r.GET("/getPubKey", func(c *gin.Context) {
if err := GenRsaKey(1024); err != nil {
fmt.Println("密钥文件生成失败!")
c.JSON(500,gin.H{
"message": "server error",
})
return
}
c.JSON(200, gin.H{
"message": publicKeyStr,
})
})
r.POST("/login", func(context *gin.Context) {
type Param struct {
EncryptPwd string `json:"encryptPwd"`
}
var par Param
err:=context.BindJSON(&par)
if err!=nil{
fmt.Println(err.Error())
context.JSON(200,gin.H{
"message":"password error",
})
}
fmt.Println("par.EncryptPwd is",par.EncryptPwd)
data, err := base64.StdEncoding.DecodeString(par.EncryptPwd)
if err != nil {
fmt.Println(err.Error())
return
}
passWord,err:=RsaDecrypt(data)
if err!=nil{
fmt.Println(err.Error())
return
}
fmt.Println("password is",string(passWord))
context.JSON(200,gin.H{
"message":string(passWord),
})
})
r.Run(":8081") // listen and serve on 0.0.0.0:8081
}
func main() {
StartServer()
}
func RsaEncrypt(origData []byte) ([]byte, error) {
//解密pem格式的公钥
block, _ := pem.Decode([]byte(publicKeyStr))
if block == nil {
return nil, errors.New("public key error")
}
// 解析公钥
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
// 类型断言
pub := pubInterface.(*rsa.PublicKey)
//加密
return rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
}
func RsaDecrypt(ciphertext []byte) ([]byte, error) {
//解密
block, _ := pem.Decode([]byte(privateKeyStr))
if block == nil {
return nil, errors.New("private key error!")
}
//解析PKCS1格式的私钥
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
// 解密
data,err := rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)
if err!=nil{
fmt.Println(err.Error())
return nil,err
}
return data,nil
}
func GenRsaKey(bits int) error {
// 生成私钥文件
privateKey, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return err
}
derStream := x509.MarshalPKCS1PrivateKey(privateKey)
priBlock := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: derStream,
}
privateKeyStr = string(pem.EncodeToMemory(priBlock))
fmt.Printf("=======私钥文件内容=========%v\n", privateKeyStr)
// 生成公钥文件
publicKey := &privateKey.PublicKey
derPkix, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return err
}
publicBlock := &pem.Block{
Type: "PUBLIC KEY",
Bytes: derPkix,
}
publicKeyStr = string(pem.EncodeToMemory(publicBlock))
fmt.Printf("=======公钥文件内容=========%v\n", publicKeyStr)
if err != nil {
return err
}
return nil
}
结果
后端将解析出来的结果返回到了前端(其实没有必要)
后端结果如下:
可以看到能正常解密