非对称加密就是有两把秘钥,任何一把加密需要用另一把才能解密,我们一般会用公钥、私钥来表明。通过这个说法我们应该就能感觉到这种非对称加密应该更复杂,同时处理速度也比对称加密要更慢但安全性要更强。
public static void main(String[] args) throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchPaddingException, Base64DecodingException, UnsupportedEncodingException { //非对称加密算法 String algorithm = "RSA"; //秘钥生成器 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm); //秘钥生成对象 KeyPair keyPair = keyPairGenerator.generateKeyPair(); //公钥 PublicKey publicKey = keyPair.getPublic(); //私钥 PrivateKey privateKey = keyPair.getPrivate(); byte[] publicKeyByte = publicKey.getEncoded(); byte[] privateByte = privateKey.getEncoded(); //用Base64处理可读性 String publicEncode = Base64.encode(publicKeyByte); String privateEncode = Base64.encode(privateByte); System.out.println("public key: " + publicEncode); System.out.println("private key: " + privateEncode); String content = "明文"; //公私钥加解密 String encodeContent = encodeByKey(publicKey, content, algorithm); System.out.println("Encode Content:" + encodeContent); String decodeContent = decodeByKey(privateKey, encodeContent, algorithm); System.out.println("Decode Content:" + decodeContent); }
public static String encodeByKey(Key key,String content,String algorithm) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.ENCRYPT_MODE,key); byte[] encodeByte = cipher.doFinal(content.getBytes()); return Base64.encode(encodeByte); } public static String decodeByKey(Key key,String encodeContent,String algorithm) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, Base64DecodingException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException { Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.DECRYPT_MODE,key); //Base64相应转换 byte[] encodeByte = Base64.decode(encodeContent); byte[] decodeByte = cipher.doFinal(encodeByte); return new String(decodeByte,"UTF-8"); }
我们一般使用像这种私钥、公钥我们一般是存放在文件中的,通过前面代码cipher.init有入参Key,所以我们一般就先需要从文件中将内容读取出来,然后再将其转换为Key对象。
1、PrivateKey生成
/** * @param encodeContent 从文本中读取到的内容 (其实也可以不做转换、直接以byte[]为入参) * @param algorithm * @return * @throws ....... */ public static PrivateKey getPrivateKey(String encodeContent,String algorithm) throws NoSuchAlgorithmException, Base64DecodingException, InvalidKeySpecException { //创建秘钥的工厂 KeyFactory keyFactory = KeyFactory.getInstance(algorithm); //创建秘钥的规则 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decode(encodeContent)); return keyFactory.generatePrivate(pkcs8EncodedKeySpec); }
2、PublicKey
public static PublicKey getPublicKey(String encodeContent,String algorithm) throws NoSuchAlgorithmException, Base64DecodingException, InvalidKeySpecException { KeyFactory keyFactory = KeyFactory.getInstance(algorithm); X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decode(encodeContent)); return keyFactory.generatePublic(x509EncodedKeySpec); }
我们在前面有讲数字摘要,就是对消息内容进行散列函数运算产生固定长度的一串内容,那这个数字签名与其有什么区别呢?简单来说数字签名是对非对称的公、私钥以及数字签名的应用,由此来确保数据的安全以及不可伪造的传输。我们前面有提到,使用非对称加密会比较复杂,所以我们应该来减少加密的内容来提高加密的速度。
例如我们要对一段文本进行签名,就像我们在合同签署自己的名字,这个"自己签署的名称"就与数字签名是一样的意义。不过在你签名前你需要确保你已经看了这个合同的内容,并且这个合同在你签名后不能再有任何的修改了,你的签名表明的其实是对这些的确认。这个过程对应到数字签名其实就是:首先对消息的内容进行数字摘要确认这个消息的内容以及不能再被修改了(同时也减少了加密的长度),然后再对这个摘要用你的私钥进行加密,由于这个私钥只有你有,等于就是你的签名了。然后别人再用你提供的公钥进行解密确认。
同时对于公钥、私钥的传输可能会被伪造,所以就需要一个结构来进行确认(相当于发证件的结构),就是CA认证。这个具体的过程可以看下这篇文章,讲的很详细。
public static void main(String[] args) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, Base64DecodingException { String content = "明文"; //非对称加密算法 String algorithm = "RSA"; //秘钥生成器 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm); //秘钥生成对象 KeyPair keyPair = keyPairGenerator.generateKeyPair(); //公钥 PublicKey publicKey = keyPair.getPublic(); //私钥 PrivateKey privateKey = keyPair.getPrivate(); //签名算法 String signatureAlgorithm = "sha256withrsa"; String signatureContent = getSignature(content, privateKey, signatureAlgorithm); System.out.println("Signature Content: " + signatureContent); boolean verifyResult = verifySignature(signatureContent, content, publicKey, signatureAlgorithm); System.out.println("Verify Result: " + verifyResult); String errorContent = "错文"; boolean errorVerifyResult = verifySignature(signatureContent, errorContent, publicKey, signatureAlgorithm); System.out.println("Error Content Verify Result: " + errorVerifyResult); }
/** * * @param content 明文内容 * @param privateKey 私钥 * @param signatureAlgorithm 签名算法 * @return * @throws ........ */ public static String getSignature(String content,PrivateKey privateKey,String signatureAlgorithm) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { //前面对象 Signature signature = Signature.getInstance(signatureAlgorithm); //初始化签名 signature.initSign(privateKey); //传入数据 signature.update(content.getBytes()); //进行进行签名 byte[] signBytes = signature.sign(); //base64可读性处理 return Base64.encode(signBytes); }
/** * * @param base64Content 签名后转换为base64的内容 * @param content 原文 * @param publicKey 公钥 * @param signatureAlgorithm 签名算法 * @return * @throws ..... */ public static boolean verifySignature(String base64Content,String content,PublicKey publicKey,String signatureAlgorithm) throws NoSuchAlgorithmException, InvalidKeyException, Base64DecodingException, SignatureException { Signature signature = Signature.getInstance(signatureAlgorithm); //初始化校验 signature.initVerify(publicKey); //原文 signature.update(content.getBytes()); //前面校验,需要传入签名的内容 return signature.verify(Base64.decode(base64Content.getBytes())); }