非对称加密加签

266 阅读3分钟

一,为什么要加签

目的1,加签是为了验签,这样说起来有点废话的意思,为了防止同一个请求多次被响应,服务端发现这个签名已经被处理过了,不再处理,防止浪费资源及对数据产生影响,也是解决重放攻击的思路之一。

目的2,防止黑客劫持你的请求,修改了参数,你的服务拿到请求,比较参数和签名,发现不对应,即被人修改过了,拒绝响应。

二,加签和加密用公钥还是私钥。

结论:私钥加签、公钥加密

1,公私钥是什么

简述下,不详细展开了:对称加密安全性不高,使用非对称加密:公钥和私钥,公钥加密只能用对应的私钥解密,反之亦然,这样双方交换1次秘钥就可以了,大大提升了安全性。

2,为什么私钥加签、公钥加密 因为加签请求被拦截,但拦截方拿不到私钥,伪造不了签名,如果用公钥加签,一般公钥是公开的,签名很容易伪造。 同样的,加密请求被拦截,拦截方拿不到接收方的私钥,解密不了数据,如果用私钥加密,拦截方拿公钥就能破解数据了。

3,非对称加密安全是安全,加解密效率极低,所以传输过程中,会同时使用对称和非对称加密: 把密码放在请求中,告诉对方,我们用这个密码加密的,你用这个密码解密就好。 密码用非对称加密,这样既有安全性又有速度,https就是这么搞的。

三,加密demo

1,生成指定长度的秘钥,这个就是对称加密的密码,使用方要用这个来解密数据

length指定长度,128,256都可以

public static byte[] generateKey(int length) throws NoSuchAlgorithmException {
    KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
    keyGenerator.init(length);
    SecretKey key = keyGenerator.generateKey();
    return key.getEncoded();
}

2,对string类型(即数据)加密

public static String encryptData(byte[] key, String data) {
    try {
        SecretKey securekey = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(1, securekey);
        byte[] db = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(db);
    } catch (Exception e) {
        throw new RuntimeException("加密失败!", e);
    }
}

对object加密怎么办?转成json不就行了

3,对密码使用非对称加密

public static String encryptKey(PublicKey publicKey, byte[] key) throws Exception {
    /** 得到Cipher对象来实现对源数据的RSA加密 */
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, publicKey);
    /** 执行加密操作 */
    byte[] b1 = cipher.doFinal(key);
    return Base64.getEncoder().encodeToString(b1);
}

PublicKey怎么创建呢?

public static PublicKey getPublicKey(String key) throws Exception {
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PublicKey publicKey = keyFactory.generatePublic(keySpec);
    return publicKey;
}

key即是接收方的公钥key,怎么生成对称,后面再讲。

4,加签

public static String sign(PrivateKey privateKey, Map respCom) throws Exception {
    try {
        return Base64.getEncoder().encodeToString(genSignature(toSignContent(respCom).getBytes("UTF-8"), privateKey, null));
    } catch (Exception ex) {
        throw new Exception("加签异常", ex);
    }
}

private static byte[] genSignature(byte[] input, PrivateKey key, String signAlgorithm) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException, NoSuchProviderException {
    signAlgorithm = StringUtils.isEmpty(signAlgorithm) ? "SHA256withRSA" : signAlgorithm;
    Signature sig = Signature.getInstance(signAlgorithm);
    sig.initSign(key);
    sig.update(input);
    return sig.sign();
}

privateKey即是自己的私钥,

public static PrivateKey getPrivateKey(String key) throws Exception {
    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
    return privateKey;
}

respCom是请求参数转成map,有多种方法,我提供rt包的一种

public static Map<String, Object> objectToMap(Object obj) {
    if (obj == null)
        return null;
    Map<String, Object> map = new HashMap<String, Object>();
    BeanInfo beanInfo = null;
    try {
        beanInfo = Introspector.getBeanInfo(obj.getClass());
    } catch (IntrospectionException e) {
        e.printStackTrace();
    }
    PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
    for (PropertyDescriptor property : propertyDescriptors) {
        String key = property.getName();
        if (key.compareToIgnoreCase("class") == 0) {
            continue;
        }
        Method getter = property.getReadMethod();
        Object value = null;
        try {
            value = getter != null ? getter.invoke(obj) : null;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        map.put(key, value);
    }
    return map;
}

四,下面说一下如何生成公私钥

百度上有很多种python、工具、app很多,我使用的是keyPairGenerator

public void test() throws Exception {
    // 初始化KeyPairGenerator
    KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
    keyGen.initialize(1024);

    // 生成一个密钥对
    KeyPair pair = keyGen.generateKeyPair();

    // 获取公钥和私钥
    byte[] publicKey = pair.getPublic().getEncoded();
    byte[] privateKey = pair.getPrivate().getEncoded();

    // 编码为Base64字符串以便打印
    String publicKeyEncoded = Base64.getEncoder().encodeToString(publicKey);
    String privateKeyEncoded = Base64.getEncoder().encodeToString(privateKey);

    // 打印公私钥
    System.out.println("Public Key: " + publicKeyEncoded);
    System.out.println("Private Key: " + privateKeyEncoded);
}