API安全接口安全设计

如何保证外网开放接口的安全性。

  1. 使用加签名方式,防止数据篡改
  2. 信息加密与密钥管理
  3. 搭建OAuth2.0认证授权
  4. 使用令牌方式
  5. 搭建网关实现黑名单和白名单

一令牌方式搭建搭建API开放平台

方案设计:

1第三方机构申请一个appId,通过appId去获取accessToken,每次请求获取accessToken都要把老的accessToken删掉

2第三方机构请求数据需要加上accessToken参数,每次业务处理中心执行业务前,先去dba持久层查看accessToken是否存在(可以把accessToken放到redis中,这样有个过期时间的效果),存在就说明这个机构是合法,无需要登录就可以请求业务数据。不存在说明这个机构是非法的,不返回业务数据。

3好处:无状态设计,每次请求保证都是在我们持久层保存的机构的请求,如果有人盗用我们accessToken,可以重新申请一个新的taken.

 

 

二基于OAuth2.0协议方式

原理

第三方授权,原理和1的令牌方式一样

1假设我是服务提供者A,我有开发接口,外部机构B请求A的接口必须申请自己的appid(B机构id)

2当B要调用A接口查某个用户信息的时候,需要对应用户授权,告诉A,我愿同意把我的信息告诉B,A生产一个授权token给B。

3B使用token获取某个用户的信息。

 

 联合微信登录总体处理流程

1 :用户同意授权,获取code

2 :通过code换取网页授权access_token

3  :通过access_token获取用户openId

4  :通过openId获取用户信息

三信息加密与密钥管理

单向散列加密
对称加密
非对称加密
安全密钥管理

1单向散列加密

散列是信息的提炼,通常其长度要比信息小得多,且为一个固定长度。加密性强的散列一定是不可逆的,这就意味着通过散列结果,无法推出任何部分的原始信息。任何输入信息的变化,哪怕仅一位,都将导致散列结果的明显变化,这称之为雪崩效应。散列还应该是防冲突的,即找不出具有相同散列结果的两条信息。具有这些特性的散列结果就可以用于验证信息是否被修改。
单向散列函数一般用于产生消息摘要,密钥加密等,常见的有:
1、MD5(Message Digest Algorithm 5):是RSA数据安全公司开发的一种单向散列算法,非可逆,相同的明文产生相同的密文。
2、SHA(Secure Hash Algorithm):可以对任意长度的数据运算生成一个160位的数值;
SHA-1与MD5的比较
因为二者均由MD4导出,SHA-1和MD5彼此很相似。相应的,他们的强度和其他特性也是相似,但还有以下几点不同:
1、对强行供给的安全性:最显著和最重要的区别是SHA-1摘要比MD5摘要长32 位。使用强行技术,产生任何一个报文使其摘要等于给定报摘要的难度对MD5是2128数量级的操作,而对SHA-1则是2160数量级的操作。这样,SHA-1对强行攻击有更大的强度。
2、对密码分析的安全性:由于MD5的设计,易受密码分析的攻击,SHA-1显得不易受这样的攻击。
3、速度:在相同的硬件上,SHA-1的运行速度比MD5慢。

1、特征:雪崩效应、定长输出和不可逆。
2、作用是:确保数据的完整性。
3、加密算法:md5(标准密钥长度128位)、sha1(标准密钥长度160位)、md4、CRC-32
4、加密工具:md5sum、sha1sum、openssl dgst。
5、计算某个文件的hash值,例如:md5sum/shalsum FileName,openssl dgst –md5/-sha

2对称加密

 

秘钥:加密解密使用同一个密钥、数据的机密性双向保证、加密效率高、适合加密于大数据大文件、加密强度不高(相对于非对称加密)

对称加密优缺点

优点:与公钥加密相比运算速度快。

缺点:不能作为身份验证,密钥发放困难

DES

是一种对称加密算法,加密和解密过程中,密钥长度都必须是8的倍数
 

  1.  
     
  2.  
    public class DES {
  3.  
    public DES() {
  4.  
    }
  5.  
     
  6.  
    // 测试
  7.  
    public static void main(String args[]) throws Exception {
  8.  
    // 待加密内容
  9.  
    String str = "123456";
  10.  
    // 密码,长度要是8的倍数 密钥随意定
  11.  
    String password = "12345678";
  12.  
    byte[] encrypt = encrypt(str.getBytes(), password);
  13.  
    System.out.println("加密前:" +str);
  14.  
    System.out.println("加密后:" + new String(encrypt));
  15.  
    // 解密
  16.  
    byte[] decrypt = decrypt(encrypt, password);
  17.  
    System.out.println("解密后:" + new String(decrypt));
  18.  
    }
  19.  
     
  20.  
    /**
  21.  
    * 加密
  22.  
    *
  23.  
    * @param datasource
  24.  
    * byte[]
  25.  
    * @param password
  26.  
    * String
  27.  
    * @return byte[]
  28.  
    */
  29.  
    public static byte[] encrypt(byte[] datasource, String password) {
  30.  
    try {
  31.  
    SecureRandom random = new SecureRandom();
  32.  
    DESKeySpec desKey = new DESKeySpec(password.getBytes());
  33.  
    // 创建一个密匙工厂,然后用它把DESKeySpec转换成
  34.  
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
  35.  
    SecretKey securekey = keyFactory.generateSecret(desKey);
  36.  
    // Cipher对象实际完成加密操作
  37.  
    Cipher cipher = Cipher.getInstance("DES");
  38.  
    // 用密匙初始化Cipher对象,ENCRYPT_MODE用于将 Cipher 初始化为加密模式的常量
  39.  
    cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
  40.  
    // 现在,获取数据并加密
  41.  
    // 正式执行加密操作
  42.  
    return cipher.doFinal(datasource); // 按单部分操作加密或解密数据,或者结束一个多部分操作
  43.  
    } catch (Throwable e) {
  44.  
    e.printStackTrace();
  45.  
    }
  46.  
    return null;
  47.  
    }
  48.  
     
  49.  
    /**
  50.  
    * 解密
  51.  
    *
  52.  
    * @param src
  53.  
    * byte[]
  54.  
    * @param password
  55.  
    * String
  56.  
    * @return byte[]
  57.  
    * @throws Exception
  58.  
    */
  59.  
    public static byte[] decrypt(byte[] src, String password) throws Exception {
  60.  
    // DES算法要求有一个可信任的随机数源
  61.  
    SecureRandom random = new SecureRandom();
  62.  
    // 创建一个DESKeySpec对象
  63.  
    DESKeySpec desKey = new DESKeySpec(password.getBytes());
  64.  
    // 创建一个密匙工厂
  65.  
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");// 返回实现指定转换的
  66.  
    // Cipher
  67.  
    // 对象
  68.  
    // 将DESKeySpec对象转换成SecretKey对象
  69.  
    SecretKey securekey = keyFactory.generateSecret(desKey);
  70.  
    // Cipher对象实际完成解密操作
  71.  
    Cipher cipher = Cipher.getInstance("DES");
  72.  
    // 用密匙初始化Cipher对象
  73.  
    cipher.init(Cipher.DECRYPT_MODE, securekey, random);
  74.  
    // 真正开始解密操作
  75.  
    return cipher.doFinal(src);
  76.  
    }
  77.  
    }
  78.  
     
  79.  
    输出
  80.  
     
  81.  
    加密前:123456
  82.  
    加密后:>p.72|
  83.  
    解密后:123456

3非对称加密

非对称加密算法需要两个密钥:公开密钥(publickey:简称公钥)和私有密钥(privatekey:简称私钥)。

公钥与私钥是一对

  1. 公钥对数据进行加密,只有用对应的私钥才能解密
  2. 私钥对数据进行加密,只有用对应的公钥才能解密

过程:

  1. 甲方生成一对密钥,并将公钥公开,乙方使用该甲方的公钥对机密信息进行加密后再发送给甲方;
  2. 甲方用自己私钥对加密后的信息进行解密。
  3. 甲方想要回复乙方时,使用乙方的公钥对数据进行加密
  4. 乙方使用自己的私钥来进行解密。

甲方只能用其私钥解密由其公钥加密后的任何信息。

特点:

算法强度复杂

保密性比较好

加密解密速度没有对称加密解密的速度快。

对称密码体制中只有一种密钥,并且是非公开的,如果要解密就得让对方知道密钥。所以保证其安全性就是保证密钥的安全,而非对称密钥体制有两种密钥,其中一个是公开的,这样就可以不需要像对称密码那样传输对方的密钥了。这样安全性就大了很多

适用于:金融,支付领域

 

RSA加密是一种非对称加密

  1.  
    import javax.crypto.Cipher;
  2.  
    import java.security.*;
  3.  
    import java.security.interfaces.RSAPrivateKey;
  4.  
    import java.security.interfaces.RSAPublicKey;
  5.  
    import java.security.spec.PKCS8EncodedKeySpec;
  6.  
    import java.security.spec.X509EncodedKeySpec;
  7.  
    import java.security.KeyFactory;
  8.  
    import java.security.KeyPair;
  9.  
    import java.security.KeyPairGenerator;
  10.  
    import java.security.PrivateKey;
  11.  
    import java.security.PublicKey;
  12.  
    import org.apache.commons.codec.binary.Base64;
  13.  
     
  14.  
     
  15.  
    /**
  16.  
    * RSA加解密工具类
  17.  
    *
  18.  
    *
  19.  
    */
  20.  
    public class RSAUtil {
  21.  
     
  22.  
    public static String publicKey; // 公钥
  23.  
    public static String privateKey; // 私钥
  24.  
     
  25.  
    /**
  26.  
    * 生成公钥和私钥
  27.  
    */
  28.  
    public static void generateKey() {
  29.  
    // 1.初始化秘钥
  30.  
    KeyPairGenerator keyPairGenerator;
  31.  
    try {
  32.  
    keyPairGenerator = KeyPairGenerator.getInstance("RSA");
  33.  
    SecureRandom sr = new SecureRandom(); // 随机数生成器
  34.  
    keyPairGenerator.initialize(512, sr); // 设置512位长的秘钥
  35.  
    KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 开始创建
  36.  
    RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
  37.  
    RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
  38.  
    // 进行转码
  39.  
    publicKey = Base64.encodeBase64String(rsaPublicKey.getEncoded());
  40.  
    // 进行转码
  41.  
    privateKey = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
  42.  
    } catch (NoSuchAlgorithmException e) {
  43.  
    // TODO Auto-generated catch block
  44.  
    e.printStackTrace();
  45.  
    }
  46.  
    }
  47.  
     
  48.  
    /**
  49.  
    * 私钥匙加密或解密
  50.  
    *
  51.  
    * @param content
  52.  
    * @param privateKeyStr
  53.  
    * @return
  54.  
    */
  55.  
    public static String encryptByprivateKey(String content, String privateKeyStr, int opmode) {
  56.  
    // 私钥要用PKCS8进行处理
  57.  
    PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyStr));
  58.  
    KeyFactory keyFactory;
  59.  
    PrivateKey privateKey;
  60.  
    Cipher cipher;
  61.  
    byte[] result;
  62.  
    String text = null;
  63.  
    try {
  64.  
    keyFactory = KeyFactory.getInstance("RSA");
  65.  
    // 还原Key对象
  66.  
    privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
  67.  
    cipher = Cipher.getInstance("RSA");
  68.  
    cipher.init(opmode, privateKey);
  69.  
    if (opmode == Cipher.ENCRYPT_MODE) { // 加密
  70.  
    result = cipher.doFinal(content.getBytes());
  71.  
    text = Base64.encodeBase64String(result);
  72.  
    } else if (opmode == Cipher.DECRYPT_MODE) { // 解密
  73.  
    result = cipher.doFinal(Base64.decodeBase64(content));
  74.  
    text = new String(result, "UTF-8");
  75.  
    }
  76.  
     
  77.  
    } catch (Exception e) {
  78.  
    // TODO Auto-generated catch block
  79.  
    e.printStackTrace();
  80.  
    }
  81.  
    return text;
  82.  
    }
  83.  
     
  84.  
    /**
  85.  
    * 公钥匙加密或解密
  86.  
    *
  87.  
    * @param content
  88.  
    * @param privateKeyStr
  89.  
    * @return
  90.  
    */
  91.  
    public static String encryptByPublicKey(String content, String publicKeyStr, int opmode) {
  92.  
    // 公钥要用X509进行处理
  93.  
    X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyStr));
  94.  
    KeyFactory keyFactory;
  95.  
    PublicKey publicKey;
  96.  
    Cipher cipher;
  97.  
    byte[] result;
  98.  
    String text = null;
  99.  
    try {
  100.  
    keyFactory = KeyFactory.getInstance("RSA");
  101.  
    // 还原Key对象
  102.  
    publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
  103.  
    cipher = Cipher.getInstance("RSA");
  104.  
    cipher.init(opmode, publicKey);
  105.  
    if (opmode == Cipher.ENCRYPT_MODE) { // 加密
  106.  
    result = cipher.doFinal(content.getBytes());
  107.  
    text = Base64.encodeBase64String(result);
  108.  
    } else if (opmode == Cipher.DECRYPT_MODE) { // 解密
  109.  
    result = cipher.doFinal(Base64.decodeBase64(content));
  110.  
    text = new String(result, "UTF-8");
  111.  
    }
  112.  
    } catch (Exception e) {
  113.  
    // TODO Auto-generated catch block
  114.  
    e.printStackTrace();
  115.  
    }
  116.  
    return text;
  117.  
    }
  118.  
     
  119.  
    // 测试方法
  120.  
    public static void main(String[] args) {
  121.  
    /**
  122.  
    * 注意: 私钥加密必须公钥解密 公钥加密必须私钥解密
  123.  
    * // 正常在开发中的时候,后端开发人员生成好密钥对,服务器端保存私钥 客户端保存公钥
  124.  
    */
  125.  
    System.out.println("-------------生成两对秘钥,分别发送方和接收方保管-------------");
  126.  
    RSAUtil.generateKey();
  127.  
    System.out.println("公钥:" + RSAUtil.publicKey);
  128.  
    System.out.println("私钥:" + RSAUtil.privateKey);
  129.  
     
  130.  
    System.out.println("-------------私钥加密公钥解密-------------");
  131.  
    String textsr = "11111111";
  132.  
    // 私钥加密
  133.  
    String cipherText = RSAUtil.encryptByprivateKey(textsr,
  134.  
    RSAUtil.privateKey, Cipher.ENCRYPT_MODE);
  135.  
    System.out.println("私钥加密后:" + cipherText);
  136.  
    // 公钥解密
  137.  
    String text = RSAUtil.encryptByPublicKey(cipherText,
  138.  
    RSAUtil.publicKey, Cipher.DECRYPT_MODE);
  139.  
    System.out.println("公钥解密后:" + text);
  140.  
     
  141.  
    System.out.println("-------------公钥加密私钥解密-------------");
  142.  
    // 公钥加密
  143.  
    String textsr2 = "222222";
  144.  
     
  145.  
    String cipherText2 = RSAUtil.encryptByPublicKey(textsr2, RSAUtil.publicKey, Cipher.ENCRYPT_MODE);
  146.  
    System.out.println("公钥加密后:" + cipherText2);
  147.  
    // 私钥解密
  148.  
    String text2 = RSAUtil.encryptByprivateKey(cipherText2, RSAUtil.privateKey, Cipher.DECRYPT_MODE);
  149.  
    System.out.print("私钥解密后:" + text2 );
  150.  
    }
  151.  
     
  152.  
    }

 

 

四使用加签名方式,防止数据篡改

  1. 客户端:请求的数据分为2部分(业务参数,签名参数),签名参数=md5(业务参数)
  2. 服务端: 验证md5(业务参数)是否与签名参数相同

 

posted @ 2020-09-20 16:42  K____K  阅读(294)  评论(0编辑  收藏  举报