golang java rsa 加密 签名 互通

博客首页

前几天做了个通信软件,自建服务器,由于服务器用的golang(ps:个人非常喜欢golang,语法的简洁,并发的优美,还有一点就是依赖库一起编译,适用起来少了许多麻烦, 现在这么大的硬盘存储空间,根本不却那点动态库的位置,最主要的一点是opensource)。
好了,说正题,rsa加密算法是现在通用的非对成加密,细节自行bing(某谷搜索引擎也用不了)。但是各种语言的实现细节还是有点区别的,唯一不变的是它们都是 做的大数运算(算法自行bing)。

由于需要客户端加密,签名,服务器也需要加密和签名。所以做起来就有点麻烦了。干脆所有细节以服务器为准,服务器用什么算法,客户端就用什么算法。
这里golang适用了pkcs1格式的密钥,所以android中就要适用pkcs1格式。后来由于rsa签名及验证的时候一直不对,所以参照了golang的签名验证算法写了个android 版本的。所以这里rsa密钥的存储就只能用最原始的方法了,就是直接存储rsa中的三个大数:modulus、exponents和public exponents。做rsa加密解密及签名验证 都是用的这三个数。
当服务器中生成rsa公钥及私钥的时候就可以获得这三个数并保存起来。其中加密用到了modulus和public exponents,java版本的解密用到了modulus和exponents。 而golang版本的解密三个数都需要。这是因为golang中创建私钥的时候需要验证(自行查看golang源码:crypto/rsa中)。

不废话了,golang中rsa的使用自行搜索。这里就贴出android中的加密解密及签名验证代码:

package hack.android3.crypto;

import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import hack.android3.utility.Error;
import hack.android3.utility.Result;

public class Crypto {
    static int gcRsaKeyLength = 512;
    static int gcAesKeyLength = 128;
    private static SimpleDateFormat dateFormat = new SimpleDateFormat("M/d H:m:s");

    public static String getTime(long time) {
        Date date = new Date(time * 1000);
        return dateFormat.format(date);
    }

    // genUUID gen a uuid string.
    public static String genUUID() {
        return UUID.randomUUID().toString();
    }

    // getString get a string from byte array.
    public static String getString(byte[] text) {
        StringBuilder stringBuilder = new StringBuilder();
        for (byte i : text) {
            stringBuilder.append((char) i);
        }
        return stringBuilder.toString();
    }

    // genAesKey generate a random byte array.
    public static byte[] genAesKey() {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            keyGenerator.init(gcAesKeyLength);
            return keyGenerator.generateKey().getEncoded();
        } catch (NoSuchAlgorithmException e) {
            return null;
        }
    }

    // genRsaKey create a rsa key and return the modulus,public exponent and private exponent.
    public static Result<String[]> genRsaKey() {
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(gcRsaKeyLength, new SecureRandom());
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            Result<String[]> result = new Result<>();
            result.Result = new String[3];
            result.Result[0] = publicKey.getModulus().toString();
            result.Result[1] = publicKey.getPublicExponent().toString();
            result.Result[2] = privateKey.getPrivateExponent().toString();
            return result;
        } catch (NoSuchAlgorithmException e) {
            return new Result<>(e);
        }
    }

    // rsaEncrypt encrypt data use rsa algorithm.
    // 这里需要传入modulus和publicexponents。
    public static Result<byte[]> rsaEncrypt(String rsaMod, String rsaPub, byte[] text) {
        Result<PublicKey> publicKey = getPublicKey(rsaMod, rsaPub);
        if (publicKey.Error != null) {
            return new Result<>(publicKey.Error);
        }
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey.Result);
            return new Result<>(cipher.doFinal(text));
        } catch (NoSuchAlgorithmException e) {
            return new Result<>(e);
        } catch (NoSuchPaddingException e) {
            return new Result<>(e);
        } catch (InvalidKeyException e) {
            return new Result<>(e);
        } catch (IllegalBlockSizeException e) {
            return new Result<>(e);
        } catch (BadPaddingException e) {
            return new Result<>(e);
        }
    }

    // rsaDecrypt decrypt data use rsa algorithm.
    // 这里和golang不同,只需要传入modulus和exponents就可以。
    public static Result<byte[]> rsaDecrypt(String rsaMod, String rsaPri, byte[] text) {
        Result<PrivateKey> privateKey = getPrivateKey(rsaMod, rsaPri);
        if (privateKey.Error != null) {
            return new Result<>(privateKey.Error);
        }
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
            cipher.init(Cipher.DECRYPT_MODE, privateKey.Result);
            return new Result<>(cipher.doFinal(text));
        } catch (NoSuchAlgorithmException e) {
            return new Result<>(e);
        } catch (NoSuchPaddingException e) {
            return new Result<>(e);
        } catch (InvalidKeyException e) {
            return new Result<>(e);
        } catch (BadPaddingException e) {
            return new Result<>(e);
        } catch (IllegalBlockSizeException e) {
            return new Result<>(e);
        }
    }

    // rsaSign sign text and return signature.
    // 签名的时候用的是私钥,所以需要modulus和exponents。
    public static Result<byte[]> rsaSign(String rsaMod, String rsaPri, byte[] text) {
        Result<PrivateKey> privateKey = getPrivateKey(rsaMod, rsaPri);
        if (privateKey.Error != null) {
            return new Result<>(privateKey.Error);
        }
        try {
            MessageDigest sha1 = MessageDigest.getInstance("SHA1");
            sha1.update(text);
            return sign((RSAPrivateKey) privateKey.Result, sha1.digest());
        } catch (NoSuchAlgorithmException e) {
            return new Result<>(e);
        }
    }

    // rsaVerify verify signature.
    // 验证的时候用的是公钥,需要modulus和public expoentns。
    public static Result<Boolean> rsaVerify(String rsaMod, String rsaPub, byte[] text, byte[] signature) {
        Result<PublicKey> publicKey = getPublicKey(rsaMod, rsaPub);
        if (publicKey.Error != null) {
            return new Result<>(publicKey.Error);
        }
        try {
            MessageDigest sha1 = MessageDigest.getInstance("SHA1");
            sha1.update(text);
            return verify((RSAPublicKey) publicKey.Result, sha1.digest(), signature);
        } catch (NoSuchAlgorithmException e) {
            return new Result<>(e);
        }
    }

    // md5Encrypt encrypt data use md5 algorithm and hex encode result.
    public static Result<String> md5Encrypt(String data) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(data.getBytes());
            byte[] encryptedData = md5.digest();
            // get hex string
            StringBuilder stringBuilder = new StringBuilder(32);
            for (byte b : encryptedData) {
                stringBuilder.append(Integer.toHexString(0xFF & b));
            }
            return new Result<>(stringBuilder.toString());
        } catch (NoSuchAlgorithmException e) {
            return new Result<>(e);
        }
    }

    public static Result<byte[]> aesEncrypt(byte[] key, byte[] text) {
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(key));
            return new Result<>(cipher.doFinal(text));
        } catch (IllegalBlockSizeException e) {
            return new Result<>(Error.New(e.getMessage()));
        } catch (InvalidKeyException e) {
            return new Result<>(Error.New(e.getMessage()));
        } catch (BadPaddingException e) {
            return new Result<>(Error.New(e.getMessage()));
        } catch (NoSuchAlgorithmException e) {
            return new Result<>(Error.New(e.getMessage()));
        } catch (NoSuchPaddingException e) {
            return new Result<>(Error.New(e.getMessage()));
        } catch (InvalidAlgorithmParameterException e) {
            return new Result<>(Error.New(e.getMessage()));
        }
    }

    public static Result<byte[]> aesDecrypt(byte[] key, byte[] text) {
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(key));
            return new Result<>(cipher.doFinal(text));
        } catch (IllegalBlockSizeException e) {
            return new Result<>(e);
        } catch (InvalidKeyException e) {
            return new Result<>(e);
        } catch (BadPaddingException e) {
            return new Result<>(e);
        } catch (NoSuchAlgorithmException e) {
            return new Result<>(e);
        } catch (NoSuchPaddingException e) {
            return new Result<>(e);
        } catch (InvalidAlgorithmParameterException e) {
            return new Result<>(e);
        }
    }

    // 这里适用modulus和public expoennts生成公钥。
    private static Result<PublicKey> getPublicKey(String rsaMod, String rsaPub) {
        try {
            BigInteger modulusInt = new BigInteger(rsaMod, 10);
            BigInteger publicExponentInt = new BigInteger(rsaPub, 10);
            // create public key
            RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(modulusInt, publicExponentInt);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return new Result<>(keyFactory.generatePublic(publicKeySpec));
        } catch (NullPointerException e) {
            return new Result<>(e);
        } catch (NumberFormatException e) {
            return new Result<>(e);
        } catch (NoSuchAlgorithmException e) {
            return new Result<>(e);
        } catch (InvalidKeySpecException e) {
            return new Result<>(e);
        }
    }

    // 这里使用modulus和exponents生成私钥。
    private static Result<PrivateKey> getPrivateKey(String rsaMod, String rsaPri) {
        try {
            BigInteger modulusInt = new BigInteger(rsaMod, 10);
            BigInteger privateExponentInt = new BigInteger(rsaPri, 10);
            // create private key
            RSAPrivateKeySpec privateKeySpec = new RSAPrivateKeySpec(modulusInt, privateExponentInt);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return new Result<>(keyFactory.generatePrivate(privateKeySpec));
        } catch (NullPointerException e) {
            return new Result<>(e);
        } catch (NumberFormatException e) {
            return new Result<>(e);
        } catch (NoSuchAlgorithmException e) {
            return new Result<>(e);
        } catch (InvalidKeySpecException e) {
            return new Result<>(e);
        }
    }

    // rsa pkcs1 sign and verify.
    // translate by go language crypto/rsa,pkcs1.
    // 这是参照golang源码写的rsa签名方法。
    private static Result<byte[]> sign(RSAPrivateKey pri, byte[] hashed) {
        int hashLen = hashed.length;
        int tLen = hashLen;
        int k = (pri.getModulus().bitLength() + 7) / 8;
        if (k < tLen + 11) {
            return new Result<>(Error.New("the k < tLen"));
        }
        byte[] em = new byte[k];
        em[1] = (byte) 1;
        for (int i = 2; i < k - tLen - 1; i++) {
            em[i] = (byte) 0xff;
        }
        System.arraycopy(hashed, 0, em, k - hashLen, hashed.length);
        BigInteger m = new BigInteger(em);
        if (m.compareTo(pri.getModulus()) > 0) {
            return new Result<>(Error.New("the m > pri.getModulus()"));
        }
        BigInteger c = m.modPow(pri.getPrivateExponent(), pri.getModulus());
        copyWithLeftPad(em, c.toByteArray());
        return new Result<>(em);
    }

    // 这是参照golang源码写的rsa验证方法。
    private static Result<Boolean> verify(RSAPublicKey pub, byte[] hashed, byte[] sig) {
        int hashLen = hashed.length;
        int tLen = hashLen;
        int k = (pub.getModulus().bitLength() + 7) / 8;
        if (k < tLen + 11) {
            return new Result<>(Error.New("the k < tLen"));
        }
        // add sign bytes to sig head.
        byte[] sig2 = new byte[sig.length + 1];
        sig2[0] = (byte) 0;
        System.arraycopy(sig, 0, sig2, 1, sig.length);
        BigInteger c = new BigInteger(sig2);
        BigInteger m = c.modPow(pub.getPublicExponent(), pub.getModulus());
        byte[] em = leftPad(m.toByteArray(), k);

        int ok = constantTimeByteEq(em[0], (byte) 0);
        ok &= constantTimeByteEq(em[1], (byte) 1);
        ok &= constantTimeCompare(em, k - hashLen, k, hashed);
        ok &= constantTimeByteEq(em[k - tLen - 1], (byte) 0);

        for (int i = 2; i < k - tLen - 1; i++) {
            ok &= constantTimeByteEq(em[i], (byte) 0xff);
        }
        if (ok != -1) {
            return new Result<>(Error.New("ok is not -1"));
        }
        return new Result<>(true);
    }

    // 这是参照golang源码写的方法。
    private static byte[] leftPad(byte[] input, int size) {
        int n = input.length;
        if (n > size) {
            n = size;
        }
        byte[] out = new byte[size];
        if (input.length > out.length) {
            System.arraycopy(input, 1, out, out.length - n, input.length - 1);
        } else {
            System.arraycopy(input, 0, out, out.length - n, input.length);
        }
        return out;
    }

    // 这是参照golang源码写的。
    private static void copyWithLeftPad(byte[] dest, byte[] src) {
        // java's biginteger to bytearray is include sig,
        // so should delete first byte.
        int numPaddingBytes = dest.length - src.length;
        for (int i = 0; i < numPaddingBytes; i++) {
            dest[i] = 0;
        }
        if (src.length > dest.length) {
            System.arraycopy(src, 1, dest, numPaddingBytes + 1, src.length - 1);
        } else {
            System.arraycopy(src, 0, dest, numPaddingBytes, src.length);
        }
    }

    // 这是参照golang源码写的。
    // 其中有趣的是:这个方法是用来比较两个字节是否相等的,当然用==也可以,但是生成的汇编代码不一样(这是针对编译型语言,
    // 这里直接照搬过来了)。
    // x                            = 01010011
    // y                            = 00010011
    // x ^ y                       = 01000000
    // ~(x^y)                     = 10111111 => z
    // z >> 4               = 00001011
    // z & (z >> 4) = 00001011
    // z                             = 00001011
    // z >> 2               = 00000010
    // z & (z >> 4) = 00000010
    // z                             = 00000010
    // z >> 1               = 00000001
    // z & (z >> 1) = 00000000
    private static int constantTimeByteEq(byte x, byte y) {
        int z = ~(x ^ y);
        z &= z >> 4;
        z &= z >> 2;
        z &= z >> 1;
        return z;
    }

    // 这是参照golang源码写的。
    private static int constantTimeCompare(byte[] x, int start, int end, byte[] y) {
        if (end - start != y.length) {
            return 0;
        }
        byte v = 0;
        for (int i = 0; i < y.length; i++) {
            v |= x[start + i] ^ y[i];
        }
        return constantTimeByteEq(v, (byte) 0);
    }
}