http接口由于很容易篡改和捕获,所有要对其作加密和签名处理。一个常见的系统架构图如下:
其中,加密和验签都在网关层实现。
RSA加密算法是一种非对称加密算法,可以通过RSA生成一对公钥和私钥,可以用这对公钥和私钥实现加解密和签名。
加密假如A是客户端,B是服务器,B使用RSA生成一对公钥和私钥,将公钥发给A,A使用公钥对内容加密,传输给B,B使用私钥将内容解密。
验签假如A是客户端,B是服务器,A使用RSA生成一对公钥和私钥,将公钥发给B,A使用私钥制造签名,将签名和内容一起发给B,B使用公钥可以判断签名的真伪,然后决定是否处理。
例子 制造一对RSA 公钥/私钥public class ClIEnt(){ main(String... args ){ KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); keyPairGen.initialize(512);//最少为512 KeyPair keyPair = keyPairGen.generateKeyPair();//生成一对公/私钥 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();//获取私钥 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();//获取公钥 HashMap keyMap = new HashMap<String,Object>(2); keyMap.put(PUBliC_KEY,publicKey); keyMap.put(PRIVATE_KEY,privateKey); //RSAPublicKey使用Base64编码,转成String串,方便打印可见 String publicKeyString=(new BASE64Encoder()).encodeBuffer(publicKey.getEncoded()); String privateKeyString=(new BASE64Encoder()).encodeBuffer(privateKey.getEncoded()); System.out.println(publicKeyString); System.out.println(privateKeyString); //将公钥String串通过Base64解码,转变为类 byte[] keyBytes; keyBytes = (new BASE64Decoder()).decodeBuffer(publicKeyString); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); PublicKey publicKey = keyFactory.generatePublic(keySpec);//转成了类 //将私钥String串通过Base64解码,转变为类 byte[] keyBytes; keyBytes = (new BASE64Decoder()).decodeBuffer(key); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); PrivateKey privateKey = keyFactory.generatePrivate(keySpec); return privateKey;//转成了类 }}公钥加密/私钥解密
上面是获取公私钥的办法,注意在String转公私钥类的时候,使用的编码类不一样。下面是加密和解密
//公钥加密 public static byte[] encrypt(byte[] plainText,String publicKeyStr,RSAPublicKey rsaPublicKey) throws Exception { PublicKey publicKey = getPublicKey(publicKeyStr);// PublicKey publicKey = rsaPublicKey; Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE,publicKey); int inputLen = plainText.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; int i = 0; byte[] cache; while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { cache = cipher.doFinal(plainText,offSet,MAX_ENCRYPT_BLOCK); } else { cache = cipher.doFinal(plainText,inputLen - offSet); } out.write(cache,cache.length); i++; offSet = i * MAX_ENCRYPT_BLOCK; } byte[] encryptText = out.toByteArray(); out.close(); return encryptText; }//私钥解密 public static byte[] decrypt(byte[] encryptText,String privateKeyStr,RSAPrivateKey rsaPrivateKey) throws Exception { PrivateKey privateKey = getPrivateKey(privateKeyStr);// PrivateKey privateKey = rsaPrivateKey; Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE,privateKey); int inputLen = encryptText.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 对数据分段解密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_DECRYPT_BLOCK) { cache = cipher.doFinal(encryptText,MAX_DECRYPT_BLOCK); } else { cache = cipher.doFinal(encryptText,cache.length); i++; offSet = i * MAX_DECRYPT_BLOCK; } byte[] plainText = out.toByteArray(); out.close(); return plainText; }私钥加签/公钥验签
下面是签名以及验证签名
//私钥加签名 public static byte[] sign(byte[] data,String privateKeyStr) throws Exception { PrivateKey priK = getPrivateKey(privateKeyStr); Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM); sig.initSign(priK); sig.update(data); return sig.sign(); }//公钥验证 public static boolean verify(byte[] data,byte[] sign,String publicKeyStr) throws Exception { PublicKey pubK = getPublicKey(publicKeyStr); Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM); sig.initVerify(pubK); sig.update(data); return sig.verify(sign); }实际的应用
上面的例子验证了加密/解密,加签/验签的可行性,在实际的应用过程中,要实现上面的,需要准备两套签名,公钥都是公开的。
客户端需要持有客户端私钥,服务端公钥,服务端需要持有服务端私钥,客户端公钥。
这样,在A向B传参的过程中,A首先将使用B的公钥将参数加密,然后加上自己的私钥制造的签名。B接收到请求后,首先使用A的公钥验证签名的真伪,然后使用B的私钥解密参数。
回调的时候也一样,B首先使用A的公钥加密,然后使用B的私钥加签名,然后A收到后,先验签名,没有问题后用A的私钥进行解密。
在上面的过程中,A,B均向对方发送了公钥,都保留自己的私钥,所以是安全的,但是关于A和B是什么时候向对方传递的公钥,有几次握手,还是要再看看资料的。
token、签名、加密token主要是存储用户信息,工作流程如下:
用户登录,请求发送到认证服务器进行认证 认证服务器验证通过,返回一个token。 用户拿着token访问其他的API 其他服务器获取token去认证服务器认证,检查访问有效性 如果token合法,那么通过,不合法则要求重新登录签名主要是防止篡改请求,工作流程如下:
客户端先列出需要发出请求参数 将请求参数和用户的secret按照一定的规则进行排列,然后用哈希散列算法将其变为一个签名串 将所有参数发送到服务器,服务器接收请求后,用同样的规律进行组装,也用同样的hash算法组装,比较签名串的异同,如果不一样,返回异常加密主要是对不方便公开的信息进行处理,有堆成加密和非对称加密两种。对称加密有很多种算法,效率很高,因为只有一个密钥,在网络环境中,对称加密的一大缺点是密钥的管理与分配,换句话说,如何把密钥发送到需要解密你的消息的人的手里是一个问题。
非对称加密采用一个公钥和一个私钥,用公钥进行加密,用私钥进行解密。
JWT(Json web token)
这是一个标准协议,JWT提供了一种用于发布接入令牌(Access Token),并对发布的签名接入令牌进行验证的方法。流程一般如下:
在上面的过程中,令牌由三部分组成,头文件(规定了哈希散列的方式),中间内容文件,包含了返给用户的信息,签名文件,由服务器产生的secret值和前面两个文件一起生成的哈希散列值。当用户带着token访问API的时候,可以解析出中间的内容。而且中间的内容一旦被伪造,那么签名的验证肯定无法通过,所有具有防篡改的功能。
在具体实施JWT的过程中,需要注意的问题由:token的作废问题,续签问题 。特别是续签问题,由于exp修改了,整个签名串就变了,jwt 的特性天然不支持续签!
public class JWTHelper { private static final String SECRET = "X******XXXxxx**X*Xxx*"; public static String signJWT() throws IOException { JWTSigner jwtSigner=new JWTSigner(SECRET); Map<String,Object> claims = new HashMap<String,Object>(); ObjectMapper mapper = new ObjectMapper(); String JsonString = mapper.writeValueAsstring(new User(1000L,"zhangSan")); claims.put("user",JsonString); return jwtSigner.sign(claims); } public static<T> T unSignJWT(String s,Class<T> tClass) throws SignatureException,NoSuchAlgorithmException,JWTVerifyException,InvalIDKeyException,IOException { JWTVerifIEr verifIEr = new JWTVerifIEr(SECRET); Map<String,Object> claims= verifIEr.verify(s); String Json = (String)claims.get("user"); System.out.println(Json); ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.readValue(Json,tClass); }}总结
以上是内存溢出为你收集整理的web接口的安全全部内容,希望文章能够帮你解决web接口的安全所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)