zoukankan      html  css  js  c++  java
  • 编码、散列与加解密

    起因:前天去一家公司面试被问到数据加密的相关知识,完全回答不上来,回家后特地总结了一下。

    一、编码、散列与加解密

    编码:使用约定的协议对数据格式化。编码的反向操作是解码,双方并不需要专用密钥来获取真实数据。

    散列:使用特定算法获取对象的数字摘要。散列是一种特殊算法,他人几乎无法伪造与原始数据完全相同的散列值,从而保证数据不会被恶意修改。但散列算法没有逆向操作,接收方不能通过散列值还原数据。

    加密与解密:通过双方约定的密钥对信息加密。使用同一把密钥加解密的方式称为对称加密,使用不同密钥加解密的方式称为非对称加密。加密与编码类似只是计算过程必须有密钥参与,因此密钥是信息保密的关键。

    二、编码与散列基础

    编码与解码

    import java.io.IOException;
    
    import sun.misc.BASE64Decoder;
    import sun.misc.BASE64Encoder;
    
    /**
     * 编码/解码
     */
    public class Base {
        // encrypt:byte[] to String 
        public static String encryptBASE(byte[] src) {
            String dst = new BASE64Encoder().encode(src);
            return dst;
        }
    
        // decrypt:String to byte[]
        public static byte[] decryptBASE(String dst) throws IOException {
            byte[] src = new BASE64Decoder().decodeBuffer(dst);
            return src;
        }
    }

    散列算法常见的规则有MD5和SHA两种

    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    
    /**
     * 散列
     */
    public class Digest {
        enum DIGEST_TYPE {
            MD5, SHA
        }
        
        public static byte[] hash(String src, DIGEST_TYPE type) throws NoSuchAlgorithmException {
            System.out.println(type);
            MessageDigest md = MessageDigest.getInstance(type.toString()); 
            md.update(src.getBytes());
            return md.digest();
        }
    }

    我们是如何保存用户密码的?

    String password = "qwer1234";
    // 生成摘要
    byte[] digest = Digest.hash(password, DIGEST_TYPE.SHA);
    String coding = Base.encryptBASE(digest);
    System.out.println(coding);
    // 2yXy/BTNLSseevMHJB9Uj7A8MSo=

    三、对称加密——DES

    对称加密顾名思义就是双方持有同一把密钥,常用的对称加密算法是DES。

    import java.io.IOException;
    import java.security.InvalidKeyException;
    import java.security.Key;
    import java.security.NoSuchAlgorithmException;
    import java.security.SecureRandom;
    import java.security.spec.InvalidKeySpecException;
    
    import javax.crypto.BadPaddingException;
    import javax.crypto.Cipher;
    import javax.crypto.IllegalBlockSizeException;
    import javax.crypto.KeyGenerator;
    import javax.crypto.NoSuchPaddingException;
    import javax.crypto.SecretKey;
    import javax.crypto.SecretKeyFactory;
    import javax.crypto.spec.DESKeySpec;
    
    /**
     * DES对称加密
     */
    public class DES {
        public static final String ALGORITHM = "DES";
    
        // byte[] to SecretKey
        private static Key getSecretKey(byte[] src)
                throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException {
            // DES 密钥
            DESKeySpec keySpec = new DESKeySpec(src);
            // 密钥的工厂
            SecretKeyFactory factory = SecretKeyFactory.getInstance(ALGORITHM);
            // 根据提供的密钥规范(密钥材料)生成 SecretKey 对象
            return factory.generateSecret(keySpec);
        }
    
        // 获取一个能够生成SecretKey的字符串
        public static String generateKey() throws NoSuchAlgorithmException {
            SecureRandom secureRandom = new SecureRandom();
            // 此类提供(对称)密钥生成器的功能
            KeyGenerator generatory = KeyGenerator.getInstance(ALGORITHM);
            // 初始化此密钥生成器
            generatory.init(secureRandom);
            // 生成一个密钥
            SecretKey secretKey = generatory.generateKey();
            // 对密钥使用Base64编码器编码
            return Base.encryptBASE(secretKey.getEncoded());
        }
    
        // 加密
        public static byte[] encrypt(byte[] src, String secretKey)
                throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, IOException,
                NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
            Key key = getSecretKey(Base.decryptBASE(secretKey));
            // 此类为加密和解密提供密码功能
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            // 用密钥初始化此 Cipher
            cipher.init(Cipher.ENCRYPT_MODE, key);
            // 按单部分操作加密或解密数据
            return cipher.doFinal(src);
        }
    
        // 解密
        public static byte[] decrypt(byte[] src, String secretKey)
                throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
                IOException, IllegalBlockSizeException, BadPaddingException {
            Key key = getSecretKey(Base.decryptBASE(secretKey));
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, key);
            return cipher.doFinal(src);
        }
    
        public static void main(String[] args) throws Exception {
            String inputStr = "DES";
            String key = DES.generateKey();
            System.err.println("原文:	" + inputStr);
            System.err.println("密钥:	" + key);
            byte[] outputData = DES.encrypt(inputStr.getBytes(), key);
    
            System.err.println("加密后:	" + Base.encryptBASE(outputData));
    
            outputData = DES.decrypt(outputData, key);
    
            System.err.println("解密后:	" + new String(outputData));
        }
    }

    控制台输出:

    原文:    DES
    密钥:    /W3mroXN1RA=
    加密后:    Z1Z3eWISSKM=
    解密后:    DES

    四、非对称加密——RSA

    非对称加密实现逻辑要比对称加密复杂的多。首先服务器端需要生成一对公钥(PublicKey)与私钥(PrivateKey)——并持有私钥,将公钥发布出去供客户端使用。当发布数据时,服务器端利用私钥与数据生成“数字签名”(signature),该签名可以理解为发布者对应发布信息的数据摘要,从而禁止第三方伪造数据内容或篡改发布主体。接着使用私钥加密(encrypt)数据。客户端接收到数据后首先利用签名信息和公钥对密文(cryptograph)进行验证(verify),再通过公钥解密(decrypt)。而客户端向服务器发送请求的时候,首先利用公钥对原文(src)加密,服务器端则通过私钥解密。综上所述,整个流程至少应该包含以下方法:

    1. signature(byte[] src, String privateKey) // 生成数字签名
    2. verify(byte[] cryptograph, String signature, String publicKey) // 验证签名
    3. encryptByPteKey(byte[] src, String privateKey) // 使用私钥加密数据
    4. decryptByPubKey(byte[] cryptograph, String publicKey) // 使用公钥解密
    5. encryptByPubKey(byte[] src, String publicKey) // 使用公钥加密数据
    6. decryptByPteKey(byte[] cryptograph, String privateKey) // 使用私钥解密

    下面是具体实现

    import java.io.IOException;
    import java.security.InvalidKeyException;
    import java.security.KeyFactory;
    import java.security.KeyPair;
    import java.security.KeyPairGenerator;
    import java.security.NoSuchAlgorithmException;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    import java.security.Signature;
    import java.security.SignatureException;
    import java.security.spec.InvalidKeySpecException;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.security.spec.X509EncodedKeySpec;
    
    import javax.crypto.BadPaddingException;
    import javax.crypto.Cipher;
    import javax.crypto.IllegalBlockSizeException;
    import javax.crypto.NoSuchPaddingException;
    
    /**
     * RSA非对称加密
     */
    public class RSA {
        public static final String KEY_ALGORITHM = "RSA";
        public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
        private static PublicKey publicKey;
        private static PrivateKey privateKey;
    
        // 生成私钥和公钥
        public static void generateKeys() throws NoSuchAlgorithmException {
            // KeyPairGenerator 类用于生成公钥和私钥对
            KeyPairGenerator pairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
            // 生成一个密钥对
            KeyPair keyPair = pairGenerator.generateKeyPair();
            // 返回对此密钥对的公钥组件的引用
            publicKey = keyPair.getPublic();
            // 返回对此密钥对的私钥组件的引用
            privateKey = keyPair.getPrivate();
        }
    
        // 获取公钥
        public static String getPublicKey() {
            return Base.encryptBASE(publicKey.getEncoded());
        }
    
        // 获取私钥
        public static String getPrivateKey() {
            return Base.encryptBASE(privateKey.getEncoded());
        }
    
        // 使用私钥对数据生成数字签名
        public static String signature(byte[] src, String privateKey) throws IOException, NoSuchAlgorithmException,
                InvalidKeySpecException, InvalidKeyException, SignatureException {
            byte[] key = Base.decryptBASE(privateKey);
            // PKCS8EncodedKeySpec类使用PKCS#8标准作为密钥规范管理的编码格式
            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(key);
            KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
            // 根据提供的密钥规范(密钥材料)生成私钥对象
            PrivateKey pteKey = factory.generatePrivate(spec);
            // Signature 类用来为应用程序提供数字签名算法
            Signature sign = Signature.getInstance(SIGNATURE_ALGORITHM);
    
            sign.initSign(pteKey);
            sign.update(src);
            return Base.encryptBASE(sign.sign());
        }
    
        // 使用公钥与数字签名验证密文
        public static boolean verify(byte[] cryptograph, String signature, String publicKey) throws IOException,
                NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
            byte[] key = Base.decryptBASE(publicKey);
            // 此类表示根据 ASN.1 类型 SubjectPublicKeyInfo 进行编码的公用密钥的 ASN.1 编码
            X509EncodedKeySpec spec = new X509EncodedKeySpec(key);
            KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
            // 根据提供的密钥规范(密钥材料)生成公钥对象
            PublicKey pubKey = factory.generatePublic(spec);
    
            Signature sign = Signature.getInstance(SIGNATURE_ALGORITHM);
            sign.initVerify(pubKey);
            sign.update(cryptograph);
    
            return sign.verify(Base.decryptBASE(signature));
        }
    
        // 使用私钥加密
        public static byte[] encryptByPteKey(byte[] src, String privateKey)
                throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException,
                InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
            byte[] key = Base.decryptBASE(privateKey);
            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(key);
            KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
            PrivateKey pteKey = factory.generatePrivate(spec);
    
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, pteKey);
    
            return cipher.doFinal(src);
        }
    
        // 使用公钥解密
        public static byte[] decryptByPubKey(byte[] cryptograph, String publicKey)
                throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException,
                InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
            byte[] key = Base.decryptBASE(publicKey);
            X509EncodedKeySpec spec = new X509EncodedKeySpec(key);
            KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
            PublicKey pubKey = factory.generatePublic(spec);
    
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, pubKey);
    
            return cipher.doFinal(cryptograph);
        }
    
        // 使用公钥加密
        public static byte[] encryptByPubKey(byte[] src, String publicKey)
                throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException,
                InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
            byte[] key = Base.decryptBASE(publicKey);
            X509EncodedKeySpec spec = new X509EncodedKeySpec(key);
            KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
            PublicKey pubKey = factory.generatePublic(spec);
    
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
    
            return cipher.doFinal(src);
        }
    
        public static byte[] decryptByPteKey(byte[] cryptograph, String privateKey)
                throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException,
                InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
            byte[] key = Base.decryptBASE(privateKey);
            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(key);
            KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
            PrivateKey pteKey = factory.generatePrivate(spec);
    
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, pteKey);
    
            return cipher.doFinal(cryptograph);
        }
    
        public static void main(String[] args)
                throws NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException, NoSuchPaddingException,
                IllegalBlockSizeException, BadPaddingException, IOException {
            RSA.generateKeys();
            String pubKey = RSA.getPublicKey();
            String pteKey = RSA.getPrivateKey();
            System.err.println("公钥: 
    
    " + pubKey);
            System.err.println("私钥: 
    
    " + pteKey);
            String inputStr = "Hello";
            byte[] data = inputStr.getBytes();
    
            byte[] encodedData = RSA.encryptByPteKey(data, pteKey);
            byte[] decryptData = RSA.decryptByPubKey(encodedData, pubKey);
    
            String outputStr = new String(decryptData);
            System.err.println("客户端获取信息: 
    
    " + outputStr);
            
            inputStr = "Hi";
            data = inputStr.getBytes();
            encodedData = RSA.encryptByPubKey(data, pubKey);
            decryptData = RSA.decryptByPteKey(encodedData, pteKey);
            
            outputStr = new String(decryptData);
            System.err.println("服务器端获取信息: 
    
    " + outputStr);
        }
    }

    控制台输出:

    公钥: 
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDKf90/9LHg4YGigdQk/MaLSjciwW4NX76K3c5L
    NAr/TiZCYUDGWFiAgYIegK/Ymr0fqW0vA5hIULKkCTuP4FYn3DEWBxZ57OHKgy+BbVyiHcY7KWBC
    OSVijFWgSgjWg9BFiUQ2b63RIwbTpYEDVDzDYyqJUfqdh4/bVVr/4+WUbwIDAQAB
    私钥: MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMp
    /3T/0seDhgaKB1CT8xotKNyLB bg1fvordzks0Cv9OJkJhQMZYWICBgh6Ar9iavR+pbS8DmEhQsqQJO4/gVifcMRYHFnns4cqDL4Ft XKIdxjspYEI5JWKMVaBKCNaD0EWJRDZvrdEjBtOlgQNUPMNjKolR+p2Hj9tVWv/j5ZRvAgMBAAEC gYATliiNXhqyeL10PYCKj1SY9nW8y97cNk2U2v1wMrl5llKHCycbyEHPNDekwafAmL8ASAACkyNw ozWUPjxfn0BV8Jm5b90NWxXyyS5DJrm4DhvB3d+4e5B1YfxxvkVzvnc+J/R/jZAQ7ToAPQivyXvJ uUJgEEerHoho9jVFYOi6yQJBAOs47ORW7jRL/KWt0GKNx+laTX9hGH/SqHNdYEfX8aC2PupfdPw3 77PSJcLkcIlgsUT/guk+XCaS/mtkc/IpY30CQQDcYvovScZGCo/o2lK0G1mqosWoUTXy9G4n60wi sWX16+vrEtB4xCrNTjlfkMnKMLWNv2LdN191HlJzD3sYs8NbAkEA44jORlb81yPGAfIvyJXDkqwi mRwwWb1J60ahEv4FouOH2ql5/VySh4y5sFvPrGQXNlo/pSYId9vrNbEXI2H79QJAeozDaGZSzgHz kl1NHgATdXJ8DSPTpx1K4AHU3XneI8kj8B0PNgiHcJDuEHk37Kn3WzIwrKis+Th6SqcyIUNc/wJA Bp6Ep7qOqxce1I/SZc2lKYccLarjBJravkti6B0tBMKgiLU/iOS0UlUgx/n/De9C6rej0QihoUU6 UNwKlfg96A==

    客户端获取信息: Hello 服务器端获取信息: Hi

    后记:无

  • 相关阅读:
    MemCached总结二:数据管理指令
    MemCached总结一:Unbutu操作系统下memcached服务器安装和telnet方式连接memcache
    Laravel5 开启Debug
    状压dp
    树形dp
    区间dp
    线性dp
    背包九讲
    dp求解各种子串子序列
    线段树详解
  • 原文地址:https://www.cnblogs.com/learnhow/p/7226003.html
Copyright © 2011-2022 走看看