go与java互用的AES实现

终于实现了go与java互用的AES算法实现。基于go可以编译windows与linux下的命令行工具,十分方便。

  • Java源码
import java.security.GeneralSecurityException;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AES {

        public static byte[] encrypt(String key, byte[] origData) throws GeneralSecurityException {

                byte[] keyBytes = getKeyBytes(key);
                byte[] buf = new byte[16];
                System.arraycopy(keyBytes, 0, buf, 0, keyBytes.length > buf.length ? keyBytes.length : buf.length);
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(buf, "AES"), new IvParameterSpec(keyBytes));
                return cipher.doFinal(origData);

        }

        public static byte[] decrypt(String key, byte[] crypted) throws GeneralSecurityException {
                byte[] keyBytes = getKeyBytes(key);
                byte[] buf = new byte[16];
                System.arraycopy(keyBytes, 0, buf, 0, keyBytes.length > buf.length ? keyBytes.length : buf.length);
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(buf, "AES"), new IvParameterSpec(keyBytes));
                return cipher.doFinal(crypted);
        }

        private static byte[] getKeyBytes(String key) {
                byte[] bytes = key.getBytes();
                return bytes.length == 16 ? bytes : Arrays.copyOf(bytes, 16);
        }

        public static String encrypt(String key, String val) throws GeneralSecurityException {
                byte[] origData = val.getBytes();
                byte[] crypted = encrypt(key, origData);
                return Base64.Encoder.RFC4648_URLSAFE.encodeToString(crypted);
        }

        public static String decrypt(String key, String val) throws GeneralSecurityException {
                byte[] crypted = Base64.Decoder.RFC4648_URLSAFE.decode(val);
                byte[] origData = decrypt(key, crypted);
                return new String(origData);
        }

        /**
         * @param args
         * @throws Exception
         */
        public static void main(String[] args) throws Exception {

                if (args.length != 3) {
                        System.err.print("Usage: java AES (-e|-d) <key> <content>");
                }
                if ("-e".equals(args[0])) {
                        System.out.println(encrypt(args[1], args[2]));
                } else if ("-d".equals(args[0])) {
                        System.out.println(decrypt(args[1], args[2]));
                } else {
                        System.err.print("Usage: java AES (-e|-d) <key> <content>");
                }
        }

}
  • Go源码
package main

import (
        "bytes"
        "crypto/aes"
        "crypto/cipher"
        "encoding/base64"
        "os"
)

func getKeyBytes(key string) []byte {
        keyBytes := []byte(key)
        switch l := len(keyBytes); {
        case l < 16:
                keyBytes = append(keyBytes, make([]byte, 16-l)...)
        case l > 16:
                keyBytes = keyBytes[:16]
        }
        return keyBytes
}

func encrypt(key string, origData []byte) ([]byte, error) {
        keyBytes := getKeyBytes(key)
        block, err := aes.NewCipher(keyBytes)
        if err != nil {
                return nil, err
        }
        blockSize := block.BlockSize()
        origData = PKCS5Padding(origData, blockSize)
        blockMode := cipher.NewCBCEncrypter(block, keyBytes[:blockSize])
        crypted := make([]byte, len(origData))
        blockMode.CryptBlocks(crypted, origData)
        return crypted, nil
}

func decrpt(key string, crypted []byte) ([]byte, error) {
        keyBytes := getKeyBytes(key)
        block, err := aes.NewCipher(keyBytes)
        if err != nil {
                return nil, err
        }
        blockSize := block.BlockSize()
        blockMode := cipher.NewCBCDecrypter(block, keyBytes[:blockSize])
        origData := make([]byte, len(crypted))
        blockMode.CryptBlocks(origData, crypted)
        origData = PKCS5UnPadding(origData)
        return origData, nil
}

func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
        padding := blockSize - len(ciphertext)%blockSize
        padtext := bytes.Repeat([]byte{byte(padding)}, padding)
        return append(ciphertext, padtext...)
}

func PKCS5UnPadding(origData []byte) []byte {
        length := len(origData)
        unpadding := int(origData[length-1])
        return origData[:(length - unpadding)]
}

func Encrypt(key string, val string) (string, error) {
        origData := []byte(val)
        crypted, err := encrypt(key, origData)
        if err != nil {
                return "", err
        }
        return base64.URLEncoding.EncodeToString(crypted), nil
}

func Decrypt(key string, val string) (string, error) {
        crypted, err := base64.URLEncoding.DecodeString(val)
        if err != nil {
                return "", err
        }
        origData, err := decrpt(key, crypted)
        if err != nil {
                return "", err
        }
        return string(origData), nil
}

func main() {

        argc := len(os.Args)
        if argc != 4 {
                os.Stdout.WriteString("usage: AES (-e|-d) <key> <content>")
                return
        }

        switch os.Args[1] {
        case "-e":
                ret, err := Encrypt(os.Args[2], os.Args[3])
                if err != nil {
                        os.Stderr.WriteString(err.Error())
                        os.Exit(1)
                }
                println(ret)
        case "-d":
                ret, err := Decrypt(os.Args[2], os.Args[3])
                if err != nil {
                        os.Stderr.WriteString(err.Error())
                        os.Exit(1)
                }
                println(ret)
        default:
                os.Stdout.WriteString("usage: AES (-e|-d) <key> <content>")
        }
}

使用go可以编译Windows与Linux下的可执行工具。