summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWillardHu <wei.hu@daocloud.io>2024-04-01 16:16:58 +0800
committerWillardHu <wei.hu@daocloud.io>2024-07-08 17:23:08 +0800
commit24880ecb39b8626550cddc435431f98eb0e58315 (patch)
tree4a677523c10e97765208e269a22dfd60bb89c5c4
parentMerge pull request #5695 from 1Shubham7/keadm-tests (diff)
downloadkubeedge-24880ecb39b8626550cddc435431f98eb0e58315.tar.gz
Encapsulate Token operations
Signed-off-by: WillardHu <wei.hu@daocloud.io>
-rw-r--r--cloud/pkg/cloudhub/cloudhub.go2
-rw-r--r--cloud/pkg/cloudhub/servers/httpserver/server.go16
-rw-r--r--cloud/pkg/cloudhub/servers/httpserver/signcerts.go76
-rw-r--r--edge/cmd/edgecore/app/server.go3
-rw-r--r--edge/pkg/edgehub/certificate/certmanager.go31
-rw-r--r--edge/pkg/edgehub/certificate/certmanager_test.go81
-rw-r--r--pkg/security/token/token.go79
-rw-r--r--pkg/security/token/token_test.go110
8 files changed, 221 insertions, 177 deletions
diff --git a/cloud/pkg/cloudhub/cloudhub.go b/cloud/pkg/cloudhub/cloudhub.go
index c3fcc0186..89efdc600 100644
--- a/cloud/pkg/cloudhub/cloudhub.go
+++ b/cloud/pkg/cloudhub/cloudhub.go
@@ -98,7 +98,7 @@ func (ch *cloudHub) Start() {
close(DoneTLSTunnelCerts)
// generate Token
- if err := httpserver.GenerateToken(); err != nil {
+ if err := httpserver.GenerateAndRefresh(beehiveContext.GetContext()); err != nil {
klog.Exit(err)
}
diff --git a/cloud/pkg/cloudhub/servers/httpserver/server.go b/cloud/pkg/cloudhub/servers/httpserver/server.go
index 4c9cddd5f..8afa1a61a 100644
--- a/cloud/pkg/cloudhub/servers/httpserver/server.go
+++ b/cloud/pkg/cloudhub/servers/httpserver/server.go
@@ -37,6 +37,7 @@ import (
hubconfig "github.com/kubeedge/kubeedge/cloud/pkg/cloudhub/config"
"github.com/kubeedge/kubeedge/common/constants"
"github.com/kubeedge/kubeedge/common/types"
+ "github.com/kubeedge/kubeedge/pkg/security/token"
)
// StartHTTPServer starts the http service
@@ -142,6 +143,7 @@ func verifyCertSubject(cert *x509.Certificate, nodeName string) error {
func verifyAuthorization(w http.ResponseWriter, r *http.Request) bool {
authorizationHeader := r.Header.Get("authorization")
if authorizationHeader == "" {
+ klog.Warning("token validation failure, token is empty")
w.WriteHeader(http.StatusUnauthorized)
if _, err := w.Write([]byte("Invalid authorization token")); err != nil {
klog.Errorf("failed to write http response, err: %v", err)
@@ -150,20 +152,16 @@ func verifyAuthorization(w http.ResponseWriter, r *http.Request) bool {
}
bearerToken := strings.Split(authorizationHeader, " ")
if len(bearerToken) != 2 {
+ klog.Warning("token validation failure, token cannot be splited")
w.WriteHeader(http.StatusUnauthorized)
if _, err := w.Write([]byte("Invalid authorization token")); err != nil {
klog.Errorf("failed to write http response, err: %v", err)
}
return false
}
- token, err := jwt.Parse(bearerToken[1], func(token *jwt.Token) (interface{}, error) {
- if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
- return nil, fmt.Errorf("there was an error")
- }
- caKey := hubconfig.Config.CaKey
- return caKey, nil
- })
+ valid, err := token.Verify(bearerToken[1], hubconfig.Config.CaKey)
if err != nil {
+ klog.Warning("token validation failure, ", err.Error())
if err == jwt.ErrSignatureInvalid {
w.WriteHeader(http.StatusUnauthorized)
if _, err := w.Write([]byte("Invalid authorization token")); err != nil {
@@ -175,10 +173,10 @@ func verifyAuthorization(w http.ResponseWriter, r *http.Request) bool {
if _, err := w.Write([]byte("Invalid authorization token")); err != nil {
klog.Errorf("Write body error %v", err)
}
-
return false
}
- if !token.Valid {
+ if !valid {
+ klog.Warning("token validation failure, valid is false")
w.WriteHeader(http.StatusUnauthorized)
if _, err := w.Write([]byte("Invalid authorization token")); err != nil {
klog.Errorf("Write body error %v", err)
diff --git a/cloud/pkg/cloudhub/servers/httpserver/signcerts.go b/cloud/pkg/cloudhub/servers/httpserver/signcerts.go
index d9e1d4d59..1acbd9eae 100644
--- a/cloud/pkg/cloudhub/servers/httpserver/signcerts.go
+++ b/cloud/pkg/cloudhub/servers/httpserver/signcerts.go
@@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
+ http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,24 +13,21 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
-
package httpserver
import (
- "crypto/sha256"
+ "context"
"crypto/x509"
- "encoding/hex"
"fmt"
"net"
- "strings"
"time"
- "github.com/golang-jwt/jwt"
certutil "k8s.io/client-go/util/cert"
"k8s.io/klog/v2"
hubconfig "github.com/kubeedge/kubeedge/cloud/pkg/cloudhub/config"
"github.com/kubeedge/kubeedge/common/constants"
+ "github.com/kubeedge/kubeedge/pkg/security/token"
)
// SignCerts creates server's certificate and key
@@ -60,28 +57,13 @@ func getIps(advertiseAddress []string) (Ips []net.IP) {
return
}
-// GenerateToken will create a token consisting of caHash and jwt Token and save it to secret
-func GenerateToken() error {
- // set double TokenRefreshDuration as expirationTime, which can guarantee that the validity period
- // of the token obtained at anytime is greater than or equal to TokenRefreshDuration
- expiresAt := time.Now().Add(time.Hour * hubconfig.Config.CloudHub.TokenRefreshDuration * 2).Unix()
-
- token := jwt.New(jwt.SigningMethodHS256)
-
- token.Claims = jwt.StandardClaims{
- ExpiresAt: expiresAt,
- }
-
- keyPEM := getCaKey()
- tokenString, err := token.SignedString(keyPEM)
-
+// GenerateAndRefresh creates a token and save it to secret, then craete a timer to refresh the token.
+func GenerateAndRefresh(ctx context.Context) error {
+ caHashToken, err := token.Create(hubconfig.Config.Ca, hubconfig.Config.CaKey,
+ hubconfig.Config.CloudHub.TokenRefreshDuration)
if err != nil {
- return fmt.Errorf("failed to generate the token for EdgeCore register, err: %v", err)
+ return fmt.Errorf("failed to generate the token for edgecore register, err: %v", err)
}
-
- caHash := getCaHash()
- // combine caHash and tokenString into caHashAndToken
- caHashToken := strings.Join([]string{caHash, tokenString}, ".")
// save caHashAndToken to secret
err = CreateTokenSecret([]byte(caHashToken))
if err != nil {
@@ -91,42 +73,18 @@ func GenerateToken() error {
t := time.NewTicker(time.Hour * hubconfig.Config.CloudHub.TokenRefreshDuration)
go func() {
for {
- <-t.C
- refreshedCaHashToken := refreshToken()
- if err := CreateTokenSecret([]byte(refreshedCaHashToken)); err != nil {
- klog.Exitf("Failed to create the ca token for edgecore register, err: %v", err)
+ select {
+ case <-t.C:
+ caHashToken, err = token.Create(hubconfig.Config.Ca, hubconfig.Config.CaKey,
+ hubconfig.Config.CloudHub.TokenRefreshDuration)
+ if err != nil {
+ klog.Error("failed to refresh the token for edgecore register, err: %v", err)
+ }
+ case <-ctx.Done():
+ break
}
}
}()
klog.Info("Succeed to creating token")
return nil
}
-
-func refreshToken() string {
- claims := &jwt.StandardClaims{}
- expirationTime := time.Now().Add(time.Hour * hubconfig.Config.CloudHub.TokenRefreshDuration * 2)
- claims.ExpiresAt = expirationTime.Unix()
- token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
- keyPEM := getCaKey()
- tokenString, err := token.SignedString(keyPEM)
- if err != nil {
- klog.Errorf("Failed to generate token signed by caKey, err: %v", err)
- }
- caHash := getCaHash()
- //put caHash in token
- caHashAndToken := strings.Join([]string{caHash, tokenString}, ".")
- return caHashAndToken
-}
-
-// getCaHash gets ca-hash
-func getCaHash() string {
- caDER := hubconfig.Config.Ca
- digest := sha256.Sum256(caDER)
- return hex.EncodeToString(digest[:])
-}
-
-// getCaKey gets caKey to encrypt token
-func getCaKey() []byte {
- caKey := hubconfig.Config.CaKey
- return caKey
-}
diff --git a/edge/cmd/edgecore/app/server.go b/edge/cmd/edgecore/app/server.go
index 6af9719de..9a86e5c9a 100644
--- a/edge/cmd/edgecore/app/server.go
+++ b/edge/cmd/edgecore/app/server.go
@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"os"
+ "strings"
ps "github.com/shirou/gopsutil/v3/process"
"github.com/spf13/cobra"
@@ -87,7 +88,7 @@ offering HTTP client capabilities to components of cloud to reach HTTP servers r
if err != nil {
klog.Exit(err)
}
- config.Modules.EdgeHub.Token = string(token)
+ config.Modules.EdgeHub.Token = strings.TrimSpace(string(token))
}
if errs := validation.ValidateEdgeCoreConfiguration(config); len(errs) > 0 {
diff --git a/edge/pkg/edgehub/certificate/certmanager.go b/edge/pkg/edgehub/certificate/certmanager.go
index fb7bc5d1f..21f0abaa8 100644
--- a/edge/pkg/edgehub/certificate/certmanager.go
+++ b/edge/pkg/edgehub/certificate/certmanager.go
@@ -5,17 +5,14 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
- "crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
- "encoding/hex"
"encoding/pem"
"fmt"
"io"
nethttp "net/http"
"os"
- "strings"
"time"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@@ -27,6 +24,7 @@ import (
"github.com/kubeedge/kubeedge/edge/pkg/edgehub/common/certutil"
"github.com/kubeedge/kubeedge/edge/pkg/edgehub/common/http"
"github.com/kubeedge/kubeedge/pkg/apis/componentconfig/edgecore/v1alpha2"
+ "github.com/kubeedge/kubeedge/pkg/security/token"
)
// jitteryDuration uses some jitter to set the rotation threshold so each node
@@ -125,13 +123,9 @@ func (cm *CertManager) applyCerts() error {
}
// validate the CA certificate by hashcode
- tokenParts := strings.Split(cm.token, ".")
- if len(tokenParts) != 4 {
- return fmt.Errorf("token credentials are in the wrong format")
- }
- ok, hash, newHash := ValidateCACerts(cacert, tokenParts[0])
- if !ok {
- return fmt.Errorf("failed to validate CA certificate. tokenCAhash: %s, CAhash: %s", hash, newHash)
+ realToken, err := token.VerifyCAAndGetRealToken(cm.token, cacert)
+ if err != nil {
+ return err
}
// save the ca.crt to file
@@ -146,7 +140,7 @@ func (cm *CertManager) applyCerts() error {
// get the edge.crt
caPem := pem.EncodeToMemory(&pem.Block{Bytes: cacert, Type: cert.CertificateBlockType})
- pk, edgeCert, err := cm.GetEdgeCert(cm.certURL, caPem, tls.Certificate{}, strings.Join(tokenParts[1:], "."))
+ pk, edgeCert, err := cm.GetEdgeCert(cm.certURL, caPem, tls.Certificate{}, realToken)
if err != nil {
return fmt.Errorf("failed to get edge certificate from the cloudcore, error: %v", err)
}
@@ -312,18 +306,3 @@ func (cm *CertManager) getCSR() (*ecdsa.PrivateKey, []byte, error) {
return pk, csr, nil
}
-
-// ValidateCACerts validates the CA certificate by hash code
-func ValidateCACerts(cacerts []byte, hash string) (bool, string, string) {
- if len(cacerts) == 0 && hash == "" {
- return true, "", ""
- }
-
- newHash := hashCA(cacerts)
- return hash == newHash, hash, newHash
-}
-
-func hashCA(cacerts []byte) string {
- digest := sha256.Sum256(cacerts)
- return hex.EncodeToString(digest[:])
-}
diff --git a/edge/pkg/edgehub/certificate/certmanager_test.go b/edge/pkg/edgehub/certificate/certmanager_test.go
deleted file mode 100644
index 38286b529..000000000
--- a/edge/pkg/edgehub/certificate/certmanager_test.go
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-Copyright 2022 The KubeEdge Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package certificate
-
-import (
- "crypto/sha256"
- "encoding/hex"
- "fmt"
- "os"
- "testing"
-
- "github.com/kubeedge/kubeedge/edge/pkg/common/util"
-)
-
-func init() {
- _, err := os.Stat("/tmp/edge.crt")
- if err != nil {
- err := util.GenerateTestCertificate("/tmp/", "edge", "edge")
-
- if err != nil {
- fmt.Printf("Failed to create certificate: %v\n", err)
- }
- }
-}
-
-func TestValidateCACerts(t *testing.T) {
- cacert, err := os.ReadFile("/tmp/edge.crt")
- if err != nil {
- t.Fatalf("Failed to load certificate: %v", err)
- }
- digest := sha256.Sum256(cacert)
- hash := hex.EncodeToString(digest[:])
-
- tests := []struct {
- cacert []byte
- hash string
- want bool
- ttName string
- }{
- {
- cacert: make([]byte, 0),
- hash: "",
- want: true,
- ttName: "empty cacert and empty hash",
- },
- {
- cacert: cacert,
- hash: hash,
- want: true,
- ttName: "valid cacert and hash",
- },
- {
- cacert: cacert,
- hash: "invalid",
- want: false,
- ttName: "invalid hash",
- },
- }
- for _, tt := range tests {
- t.Run("", func(t *testing.T) {
- got, _, _ := ValidateCACerts(tt.cacert, tt.hash)
- if got != tt.want {
- t.Errorf("ValidateCACerts = %v, want %v", got, tt.want)
- }
- })
- }
-}
diff --git a/pkg/security/token/token.go b/pkg/security/token/token.go
new file mode 100644
index 000000000..1fd183fb9
--- /dev/null
+++ b/pkg/security/token/token.go
@@ -0,0 +1,79 @@
+/*
+Copyright 2024 The KubeEdge Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package token
+
+import (
+ "crypto/sha256"
+ "encoding/hex"
+ "fmt"
+ "strings"
+ "time"
+
+ "github.com/golang-jwt/jwt"
+)
+
+// Create will creates a new token consisting of caHash and jwt token.
+func Create(ca, caKey []byte, intervalTime time.Duration) (string, error) {
+ // set double intervalTime as expirationTime, which can guarantee that the validity period
+ // of the token obtained at anytime is greater than or equal to intervalTime.
+ expiresAt := time.Now().Add(time.Hour * intervalTime * 2).Unix()
+
+ token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
+ ExpiresAt: expiresAt,
+ })
+
+ tokenString, err := token.SignedString(caKey)
+ if err != nil {
+ return "", err
+ }
+
+ // combine caHash and tokenString into caHashAndToken
+ return strings.Join([]string{hashCA(ca), tokenString}, "."), nil
+}
+
+func hashCA(ca []byte) string {
+ digest := sha256.Sum256(ca)
+ return hex.EncodeToString(digest[:])
+}
+
+// Verify verifies the token is valid
+func Verify(token string, caKey []byte) (bool, error) {
+ jwtToken, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
+ if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
+ return nil, fmt.Errorf("invalid token method type, want *jwt.SigningMethodHMAC, but is %T", token.Method)
+ }
+ return caKey, nil
+ })
+ if err != nil {
+ // return the original error for the caller to determine.
+ return false, err
+ }
+ return jwtToken.Valid, nil
+}
+
+// VerifyCAAndGetRealToken verifies the CA certificate by hashcode is same with token part,
+// then get real token, which cut prefix ca hash from input token.
+func VerifyCAAndGetRealToken(token string, ca []byte) (string, error) {
+ tokenParts := strings.Split(token, ".")
+ if len(tokenParts) != 4 {
+ return "", fmt.Errorf("token %s credentials are in the wrong format", token)
+ }
+ if currentHash := hashCA(ca); currentHash != tokenParts[0] {
+ return "", fmt.Errorf("failed to validate CA certificate. tokenCAhash: %s, CAhash: %s",
+ tokenParts[0], currentHash)
+ }
+ return strings.Join(tokenParts[1:], "."), nil
+}
diff --git a/pkg/security/token/token_test.go b/pkg/security/token/token_test.go
new file mode 100644
index 000000000..e9352474c
--- /dev/null
+++ b/pkg/security/token/token_test.go
@@ -0,0 +1,110 @@
+/*
+Copyright 2024 The KubeEdge Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package token
+
+import (
+ "encoding/pem"
+ "testing"
+)
+
+const (
+ testCA = `-----BEGIN CERTIFICATE-----
+MIIDEDCCAfigAwIBAgIIHmr3g3dw7rYwDQYJKoZIhvcNAQELBQAwJjEOMAwGA1UE
+BhMFQ2hpbmExCTAHBgNVBAoTADEJMAcGA1UECxMAMB4XDTI0MDQwODA5NTY1MloX
+DTM0MDQwNjA5NTY1MlowJjEOMAwGA1UEBhMFQ2hpbmExCTAHBgNVBAoTADEJMAcG
+A1UECxMAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm2td7Yn3tTv0
+g1d6MxQBqESl/flEvt7G1gFWoXHHzSN9+jh75Y1meHkuLu6LeYYuQMdFiHzra/jM
+mN78RJToOW96yH97x9F+YstCStKdMh3D04vmiXqwdkzIFXvbcFol1mXP8r72R8z+
+odjPr/EwDNI0KSzTtZfoKIalwCDzqX+WPOgRKaCyTHs01dNHSQhdyhG9oTdeDtIL
+e6HNqxA966jMF6p/giHSUrcec41XxxZPfHZ5sppaSIMxabBS/M/lMlav2ZMfr6+y
+szP33/CRnbn45d767wyH9P0kbWrdU9IPN9vGD7QKfNfcoN2FLHgkkoXOJl/AXJfF
+BftXWs0qoQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAqQwDwYDVR0TAQH/BAUwAwEB
+/zAdBgNVHQ4EFgQUegaSgp7zhR9AwLcVBKjraccqbkMwDQYJKoZIhvcNAQELBQAD
+ggEBAI/I2Ln//zxUhMY9JwM57sDDQ7Vimc+uWSgrtqhiGOGMzhBFREr1dS5UE1a2
+dMMh566lBuQAT7hyOC9EqL+zbHAcGZGUyIqByIKv9W2HMNnTOGZ3XbPJNV6DH/wX
+66Jv9dvNf+EVj0PhJvRmn6QslbVrOmAtmylllTXDJnoULX2+ZAgHNS2+p3rnXCas
+Nh52RfmjaH1sH7e1zvVKvOpTCKbuArzjspSdJ1ssnWYnrLtkAvz7PZEDL88fFmre
+uhDkSogDJI/yC8m+6lnvYdLWuDAkVREP39XZ/7KtJFLEeBikRhsRK2BOnVidPFDM
+rFqlS7gD0cPmIEo2wgkh3pKaxNE=
+-----END CERTIFICATE-----`
+
+ testCAKey = `-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAm2td7Yn3tTv0g1d6MxQBqESl/flEvt7G1gFWoXHHzSN9+jh7
+5Y1meHkuLu6LeYYuQMdFiHzra/jMmN78RJToOW96yH97x9F+YstCStKdMh3D04vm
+iXqwdkzIFXvbcFol1mXP8r72R8z+odjPr/EwDNI0KSzTtZfoKIalwCDzqX+WPOgR
+KaCyTHs01dNHSQhdyhG9oTdeDtILe6HNqxA966jMF6p/giHSUrcec41XxxZPfHZ5
+sppaSIMxabBS/M/lMlav2ZMfr6+yszP33/CRnbn45d767wyH9P0kbWrdU9IPN9vG
+D7QKfNfcoN2FLHgkkoXOJl/AXJfFBftXWs0qoQIDAQABAoIBAQCUnP8M48+cWj89
+1EkCTJAlIbeD+nY0+XsyKcd3yv/d9aFBwf8fCq3AZ0e1Et8FjjvuL14a3DCVZyvk
+xdx9i9HfEe1biSOId5cdyvSR7YDo6jNVtsH0FgBkrpjoii3T6i+iKmrE2LtQ/wFB
+K7u0prFmoR3FfZrXWvFgxxf5dsjn+p3nJQdDZbWjAcZJdf7T78EtnbQ4uzCAQRTW
+hsmfI8OPTzyf/FLTkscjNzP6GVMWP9x017TfKgucqwPt8FeKqc9Si+fEZ3GDQiSi
+KZHSXGIjO3MxCPDX4XW4sfkP5+iB8OEZHUHN5SHBAnTu2sR5hnwnPj2y1jcL7T3s
+Fv3vnTWRAoGBAMW0+L7PZ3foEVi8IL9pkB24+3u6CIIrc98a7YKbLfWmDSmBQis/
+5BRSvuf1l99EZDbVHE6xkhecSOP8cmIrZ1NjfUgXloaDkHg4hLgtQ+ZIPnSOHifP
+M+DO6re+yCkngxgklofUSxX4STviPJiewZayVTwWjpbK74fv8KrraooNAoGBAMk+
+goOH8aUf2QeyCHrROQAb8QXF3hRbyimFES/7eSdlFIy/bROnT9cdcSviyzrvBzwY
+3SIEcp7g3ZbJb+dFaUObE/nY6EJP+moCCOurBKjzJeRQ1zlFzb08c4lCAU7/l28z
+iHSQJavQof/mvcR5Pi81h5VFuFeFgchBYoHChCHlAoGBAJ9Om8DkzrLHxHKD5L9Y
+CFBq5flkhcadzNhRkmBTOk1eZ+yxwuemq9nUcw/lzWKScU3dmtmuK9HqlLFgkaqY
+3sFKwYB9wUTSbm7w28CseLHuNKUmfxYE2AClumwkxpSiyfeCQ+lfHsGtNxWRztIL
+2mHbgOLSKkNHcotOw9Z1q3thAoGAGA/dUxTCE9hG/uCOmwDBK/4rR2FtOEnxVh2O
+/Im45rjzSBDrXdo3daUTjwfC/PzvhIQEjLiza8O/OvRC6QgnmenE7a69tpARhPNR
+VbxRBlJsSWxRD4wFGYdM2TCHL4bn+GfU/PrvRiff9tUEA6XrhYGFAJghfnV8GxGW
+UaWMXvECgYEAlhQUBLZ/ZTPXQWb7VA5Mur/s0ptrs4CAP+KBc9xyBfKrPEmjMcxJ
+2788MXYbnGzwpY0Nk/ruUNJ+PFPy4GgGWnZp7Fqi5qGIrFkuazQaulRQJwTZkIzJ
+x0KJ+pjtT+89L1r7murZAJcPL+TyRYeg295NTfcjAfSdcBfIjzURYVg=
+-----END RSA PRIVATE KEY-----`
+)
+
+func TestToken(t *testing.T) {
+ var token string
+
+ _, caDer := pem.Decode([]byte(testCA))
+ _, cakeyDer := pem.Decode([]byte(testCAKey))
+
+ t.Run("test Create", func(t *testing.T) {
+ var err error
+ token, err = Create(caDer, cakeyDer, 1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(token) == 0 {
+ t.Fatal("failed to get token")
+ }
+ })
+
+ t.Run("test VerifyCAAndGetRealToken", func(t *testing.T) {
+ var err error
+ token, err = VerifyCAAndGetRealToken(token, caDer)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(token) == 0 {
+ t.Fatal("failed to get token")
+ }
+ })
+
+ t.Run("test Verify", func(t *testing.T) {
+ b, err := Verify(token, cakeyDer)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !b {
+ t.Fatalf("invalid token %s", token)
+ }
+ })
+}