SecureRandom生成“强随机数”用于生成RSA*公钥/私钥*window和linux下不一致的问题

1.先说下问题:
由于我们的服务部署环境是两台服务器,在服务启动时生成RSA密钥对。这有一个问题:当两台机器分别启动时,生成了不同的密钥对。而当客户端需要用到RSA加解密的时候,链接可能会被负载到另一台机器上,造成解密失败,抛出异常。

2.看下之前的代码(第1版):

private static final KeyPair keyPair = initKey();

            private static KeyPair initKey() {
                    try {
                            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
                            SecureRandom random = new SecureRandom();
                            KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");
                            generator.initialize(512, random);
                            return generator.generateKeyPair();
                    } catch (Exception e) {
                            throw new RuntimeException(e);
                    }
            }

其中SecureRandom random = new SecureRandom();在生成强随机数时,没有指定任何参数。所以生成的随机数是无法控制的。通过查询API,发现另一个构造方法new SecureRandom(byte[] b),继而可以通过指定固定参数,返回固定的SecureRandom对象。故改出第2版:

  private static final KeyPair keyPair = initKey();

            private static KeyPair initKey() {
                    try {
                            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
                            byte  b[] = "abc123".getBytes();
                            SecureRandom random = new SecureRandom(b);
                            KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");
                            generator.initialize(512, random);
                            return generator.generateKeyPair();
                    } catch (Exception e) {
                            throw new RuntimeException(e);
                    }
            }

经本地测试,可以满足每次生成的密钥对是一样的。但是打包到测试服务器后,发现没有起作用,依然是不一样的密钥对。开始抓狂……,然后各种百度,得知win和linux处理方法是不一样,根据找到的资料,重新调整下。第3版:

private static KeyPair initKey() {
                    try {
                            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
                            byte  b[] = "abc123".getBytes();
                             SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
                             secureRandom.setSeed(b);

                            KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");
                            generator.initialize(512,secureRandom);
                            return generator.generateKeyPair();
                    } catch (Exception e) {
                            throw new RuntimeException(e);
                    }
            }


经测试,win和linux都能完美兼容。到此暂告一段落。

其他两个生成publicKey和解密方法,也贴上:

  /**
                * 生成public key
                 * @return
                 */
            public static String generateBase64PublicKey() {
                    RSAPublicKey pubkey = (RSAPublicKey) keyPair.getPublic();
                    return new String(Base64.encodeBase64(pubkey.getEncoded()));
            }


            /**
                 * 解密
                 * @param string
                 * @return
                 */
            public static String decryptBase64(String string) {
                    return new String(decrypt(Base64.decodeBase64(string)));
            }

            private static byte[] decrypt(byte[] string) {
                    try {
                            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
                            Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", "BC");
                            RSAPrivateKey pbk = (RSAPrivateKey) keyPair.getPrivate();
                            cipher.init(Cipher.DECRYPT_MODE, pbk);
                            byte[] plainText = cipher.doFinal(string);
                            return plainText;
                    } catch (Exception e) {
                            throw new RuntimeException(e);
                    }
            }

又经安全考虑,觉得把用于生成秘钥的放在代码里不安全,故需要存放到数据库中。但是在写代码的时候发现,在RSAUtils工具类中,spring无法注入,调用不到service层查询数据库。经各种百度查找后,修改RSAUtils工具类初始化加载如下:

@Component
            public class RSAUtils {
                    private static final Logger log = LoggerFactory.getLogger(RSAUtils.class);

                    private static KeyPair keyPair = null;

                    @Autowired
                    private ISysParamService sysParamService;

                    private static RSAUtils rsaUtils;

                    @PostConstruct
                    public void init(){
                            rsaUtils = this;
                            rsaUtils.sysParamService = this.sysParamService;
                            keyPair = initKey();
                    }

                    private static KeyPair initKey() {
                            try {
                                    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
                                    String keyStr = rsaUtils.sysParamService.getVal("key","key"); //service方法,查询到该字符串
                                    byte  b[] = keyStr.getBytes();
                                    SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
                                    secureRandom.setSeed(b);

                                    KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");
                                    generator.initialize(512,secureRandom);
                                    return generator.generateKeyPair();
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                    }
            }

至此,完成此需求的调整开发。记录下来。

你可能感兴趣的:(java,经验分享)