首页 文章 精选 留言 我的

精选列表

搜索[表单对接],共10001篇文章
优秀的个人博客,低调大师

支付对接常用的加密方式介绍以及java代码实现

京东科技 姚永健 一、术语表: 1.对称算法 加密解密密钥是相同的。这些算法也叫秘密密钥算法或单密钥算法,它要求发送者和接收者在安全通信之前,商定一个密钥。对称算法的安全性依赖于密钥,泄漏密钥就意味着任何人都能对消息进行加密解密。只要通信需要保密,密钥就必须保密。 对称算法可分为两类。一次只对明文中的单个位(有时对字节)运算的算法称为序列算法或序列密码。另一类算法是对明文的一组位进行运算,这些位组称为分组,相应的算法称为分组算法或分组密码。现代计算机密码算法的典型分组长度为64位――这个长度大到足以防止分析破译,但又小到足以方便作用。  2.非对称算法 非对称算法也叫公开密钥加密,它是用两个数学相关的密钥对信息进行编码。在此系统中,其中一个密钥叫公开密钥,可随意发给期望与密钥持有者进行安全通信的人。公开密钥用于对信息加密。第二个密钥是私有密钥,属于密钥持有者,此人要仔细保存私有密钥。密钥持有者用私有密钥对收到的信息进行解密。 一般来说,都是公钥加密,私钥解密。如果系统双方需要相互通讯,可以生成两对密钥对。各自保存好自己的私钥和对方的公钥,用公钥加密,私钥进行解密  3.可逆加密算法 一般来说,涉及到秘钥之类的算法,都是可逆的。意思就是通过算法和秘钥加密之后,可以再次通过解密算法还原。常见的有DES、3DES、AES128、AES192、AES256 。其中AES后面的数字代表的是密钥长度。对称加密算法的安全性相对较低,比较适用的场景就是内网环境中的加解密。  4.不可逆算法 常见的不可逆加密算法有MD5,HMAC,SHA1、SHA-224、SHA-256、SHA-384,和SHA-512,其中SHA-224、SHA-256、SHA-384,和SHA-512我们可以统称为SHA2加密算法,SHA加密算法的安全性要比MD5更高,而SHA2加密算法比SHA1的要高。其中SHA后面的数字表示的是加密后的字符串长度,SHA1默认会产生一个160位的信息摘要。 不可逆加密算法最大的特点就是不需要密钥  5.加密盐 加密盐也是比较常听到的一个概念,盐就是一个随机字符串用来和我们的加密串拼接后进行加密。加盐主要是为了保证加密字符串的安全性。假如有一个加盐后的加密串,黑客通过一定手段得到这个加密串,他解密后拿到的明文,并不是我们加密前的字符串,而是加密前的字符串和盐组合的字符串,这样相对来说又增加了字符串的安全性 或者也可以用在签名,例如签名是对明文或者密文加盐后的签名,有人想串改数据,如果不知道这个盐和规则,那么接收方验签就会不通过,从而保证通讯的安全。 二、传统加密算法介绍 DES(Data Encryption Standard): 对称算法,数据加密标准,速度较快,适用于加密大量数据的场合。 AES算法: 是DES的升级版,属于对称算法。可逆 代码:AESUtil package AES; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Base64; 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.spec.SecretKeySpec; public class AESUtil { /** * AES加密字符串 * * @param content * 需要被加密的字符串 * @param password * 加密需要的密码 * @return 密文 */ public static byte[] encrypt(String content, String password) { try { KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者 kgen.init(128, new SecureRandom(password.getBytes()));// 利用用户密码作为随机数初始化出 //加密没关系,SecureRandom是生成安全随机数序列,password.getBytes()是种子,只要种子相同,序列就一样,所以解密只要有password就行 SecretKey secretKey = kgen.generateKey();// 根据用户密码,生成一个密钥 byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥,如果此密钥不支持编码,则返回 SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥 Cipher cipher = Cipher.getInstance("AES");// 创建密码器 byte[] byteContent = content.getBytes("utf-8"); cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化为加密模式的密码器 byte[] result = cipher.doFinal(byteContent);// 加密 return result; } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; } /** * 解密AES加密过的字符串 * * @param content * AES加密过过的内容 * @param password * 加密时的密码 * @return 明文 */ public static byte[] decrypt(byte[] content, String password) { try { KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者 kgen.init(128, new SecureRandom(password.getBytes())); SecretKey secretKey = kgen.generateKey();// 根据用户密码,生成一个密钥 byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥 SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥 Cipher cipher = Cipher.getInstance("AES");// 创建密码器 cipher.init(Cipher.DECRYPT_MODE, key);// 初始化为解密模式的密码器 byte[] result = cipher.doFinal(content); return result; // 明文 } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; } public static void main(String[] args) throws Exception { String a = Base64.getEncoder().encodeToString("435678(*&*&^*&^%&^$%^#%#!@#$%^&%%90|8752$".getBytes(StandardCharsets.UTF_8)); String b = new String(Base64.getDecoder().decode(a.getBytes(StandardCharsets.UTF_8))); System.out.println(a+":"+b); String content = "{'aaa':'111','bbb':'222'}"; String secretKey = "43567890|8752$"; System.out.println("需要加密的内容:" + content); byte[] encrypt = encrypt(content, secretKey); System.out.println("加密后的2进制密文:"); System.out.println(new String(encrypt)); String hexStr = Base64.getEncoder().encodeToString(encrypt); System.out.println("base64编码后密文:" + hexStr); byte[] byte2 = Base64.getDecoder().decode(hexStr); System.out.println("解码后转换为2进制后密文:"); System.out.println(new String(byte2)); byte[] decrypt = decrypt(byte2, secretKey); System.out.println("解密后的内容:" + new String(decrypt,"utf-8")); } } RSA算法: 公钥加密算法,非对称,可逆 代码:RSAUtil package RSASha256; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import java.io.UnsupportedEncodingException; import java.security.*; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import java.util.HashMap; import java.util.Map; public class RSAUtil { private static final int DEFAULT_RSA_KEY_SIZE = 2048; private static final String KEY_ALGORITHM = "RSA"; private static final String SIGNATURE_ALGORITHM = "MD5withRSA"; public static void main(String [] args){ Map<String,String> result = generateRsaKey(DEFAULT_RSA_KEY_SIZE); String publicKey = result.get("publicKey"); String privateKey = result.get("privateKey"); // String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuazymB25s/ueIfwMM2H74736dQQE4bvRDgNfWltM4PzduNC84ntav41qEgbZtby47O57m/YDVf6vMXJH25ejlsBBhls7FD7xHTkOrskZqLekH0xCs2xa/akdwllbewUNzvWMuQy8X3j2VnrILXzVjvqYxkFY0itMo8fq1xSO6B/S5/RaeKpTtIepFB2cIU9vMrtLoCsnjVoTuPdWcoC79LtS0g1Q5BV4dn+Uca+8/gUbhS7vbth3oLZt6gXTRjJrPxhT4lqWsCZqwkQHPG/8JQgnFqs7+R0KcyemG/411IaZI9WYRGTsQji8sQ2q7HAlZRXvJn+As7jJPvyi67JGKwIDAQAB"; // String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC5rPKYHbmz+54h/AwzYfvjvfp1BAThu9EOA19aW0zg/N240Lzie1q/jWoSBtm1vLjs7nub9gNV/q8xckfbl6OWwEGGWzsUPvEdOQ6uyRmot6QfTEKzbFr9qR3CWVt7BQ3O9Yy5DLxfePZWesgtfNWO+pjGQVjSK0yjx+rXFI7oH9Ln9Fp4qlO0h6kUHZwhT28yu0ugKyeNWhO491ZygLv0u1LSDVDkFXh2f5Rxr7z+BRuFLu9u2Hegtm3qBdNGMms/GFPiWpawJmrCRAc8b/wlCCcWqzv5HQpzJ6Yb/jXUhpkj1ZhEZOxCOLyxDarscCVlFe8mf4CzuMk+/KLrskYrAgMBAAECggEABjC+8dVj4J1N+2IU4g2tQT2PQSF+LCx/3tC7+B49JO8pUUUcVwy3zNUhKTKzRXziSXv2ARAlslNIcgSWYrreiGMmjB00jgs/LLM/SxKHWXmt7iEzxBmjuvtNc7JY+3QCrti+9Vh4W1KEHAQB8opL8HVobIu3M2KgLoG20a7syM5gPiCUb3EQ6P30u47y7I2wsbMnMmu79rPu3Q7lSNeoB9KdVV6EA6vukL3chY1QxFh6oFZS1yNjFjxy5RxT8U5Juhr5LYRnOP8MEVptbtrh34SSm0uyckkgJ6jOsrDJqo/DJxjPxixj+BWgG5/XbNq3PFEyEAqkrTZY+FMHSufoaQKBgQDpqPxpl05mDUXCnc7I6F8LZqjnjZU1h9zwKuQe94Fs3I3siWS62sJx1pZ66P9eeIOsT0T4Ye9nDx2x04yyfUcENhydFTQ5M5M9e2q1kKdNeBUT6Xy4WcRibKozUq07mLYSn0kDbuimp/Erp2w3hl5nZu/eLKDjt7yM0otv3uPp5wKBgQDLbYC4hcvk+f1kd/xJQ2ljwF+c8kHHFrlLRipf7qTB24dcrauWXbAGbbcKXx2G+3Waswyf09NdALiWab/jUUUHJm0YGxyr0k5lHci0/eC7hD3tWTL/HRyeF3umKZEkBQypzNeymE6t0lSqjL3MXFQLqumu4h677qA9/DNh7DYhHQKBgQCfAm/bj6s7iba6jVfmozPi91bkVRaAWlgBXL7nT/nU0ncGzC0vd6WxgJ3hQORgLtU0krFV8pfP45qKpHNwGA8XD5gDUiW68500zuM8chdYgeqeJVvJvNUHQfnFeXMIRpFJNPqkCnrqxwk5cvMTCi7+YS/FW0uWDDiVAMcBN4aUawKBgCOgvAiVNk6WEfEEqqTSL6UOzjAYpbiOnEk4src2fpiNMDnlGMYvBmM51/LzEaLQa5p6fV2Ipd4GAE4nmznew+4qprSwGudk3+IJw1sfk7qDwKzPEIVpvddaWYeShB8A22TpwWVAE5eR3M459AvUp8ubVW4RoDxd4Ka6gu1Fh31pAoGAYNPKJNCrdOsQH0nkH0Ld44cH6HD+zcZtoad2eQk0T2SPnKBsIS1S9W0adreOXUv1edO/hN3P/BcTuCaax7ijTscS2f0atc/NIa6LjBnK7oUBBzib9v21L72ZVZ5st4c/H7IzbQCXfS81489a7TTHP+e1HzS/XePftO0pAkr1GJ0="; System.out.println("公钥为:" + publicKey); System.out.println("私钥为:" + privateKey); String plaintext = "{'a':'1111','b':'2222'}"; String ciphertext = null; try { System.out.println("开始加密明文:"+plaintext); ciphertext = encrypt(plaintext,publicKey); } catch (Exception e) { System.out.println("加密失败"); throw new RuntimeException(e); } System.out.println("得到的密文为:"+ciphertext); String deciphering = decrypt(ciphertext,privateKey); System.out.println("解密后:"+deciphering); } /** * 生成RSA 公私钥,可选长度为1025,2048位. */ public static Map<String,String> generateRsaKey(int keySize) { Map<String,String> result = new HashMap<>(2); try { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); // 初始化密钥对生成器,密钥大小为1024 2048位 keyPairGen.initialize(keySize, new SecureRandom()); // 生成一个密钥对,保存在keyPair中 KeyPair keyPair = keyPairGen.generateKeyPair(); // 得到公钥字符串 result.put("publicKey", new String(Base64.getEncoder().encode(keyPair.getPublic().getEncoded()))); // 得到私钥字符串 result.put("privateKey", new String(Base64.getEncoder().encode(keyPair.getPrivate().getEncoded()))); } catch (GeneralSecurityException e) { e.printStackTrace(); } return result; } /** * RSA私钥解密 * @param str 解密字符串 * @param privateKey 私钥 * @return 明文 */ public static String decrypt(String str, String privateKey) { //64位解码加密后的字符串 byte[] inputByte; String outStr = ""; try { inputByte = Base64.getDecoder().decode(str.getBytes("UTF-8")); //base64编码的私钥 byte[] decoded = Base64.getDecoder().decode(privateKey); RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded)); //RSA解密 Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, priKey); outStr = new String(cipher.doFinal(inputByte)); } catch (UnsupportedEncodingException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | InvalidKeySpecException | NoSuchAlgorithmException e) { e.printStackTrace(); } return outStr; } /** * RSA公钥加密 * @param str 需要加密的字符串 * @param publicKey 公钥 * @return 密文 * @throws Exception 加密过程中的异常信息 */ public static String encrypt(String str, String publicKey) throws Exception { //base64编码的公钥 byte[] decoded = Base64.getDecoder().decode(publicKey); RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded)); //RSA加密 Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, pubKey); String outStr = Base64.getEncoder().encodeToString(cipher.doFinal(str.getBytes("UTF-8"))); return outStr; } public static String sign(String data, String priKey) throws Exception { byte[] decoded = Base64.getDecoder().decode(priKey); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(new X509EncodedKeySpec(decoded)); Signature signature = Signature.getInstance("MD5withRSA"); signature.initSign(privateKey); signature.update(data.getBytes()); return new String(Base64.getEncoder().encode(signature.sign())); } public static boolean verify(String pubKey, String sign, String data) throws Exception{ //获取KeyFactory,指定RSA算法 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); //将BASE64编码的公钥字符串进行解码 byte[] encodeByte = Base64.getDecoder().decode(pubKey); //将BASE64解码后的字节数组,构造成X509EncodedKeySpec对象,生成公钥对象 PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodeByte)); Signature signature = Signature.getInstance("MD5withRSA"); //加载公钥 signature.initVerify(publicKey); //更新原数据 signature.update(data.getBytes("UTF-8")); //公钥验签(true-验签通过;false-验签失败) return signature.verify(Base64.getDecoder().decode(sign)); } } MD5算法: 信息摘要(Hash安全散列)算法,也叫哈希算法,哈希值也叫散列值,不可逆,不需要秘钥。 代码: MD5Util package MD5; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class MD5Util { public static void main(String[] args) { String sha256Str = getSha256Str("123456"); System.out.println(sha256Str); } /** * sha256加密 * * @param str 要加密的字符串 * @return 加密后的字符串 */ public static String getSha256Str(String str) { MessageDigest messageDigest; String encodeStr = ""; try { messageDigest = MessageDigest.getInstance("MD5"); messageDigest.update(str.getBytes(StandardCharsets.UTF_8)); encodeStr = byte2Hex(messageDigest.digest()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return encodeStr; } /** * sha256加密 将byte转为16进制 * * @param bytes 字节码 * @return 加密后的字符串 */ private static String byte2Hex(byte[] bytes) { StringBuilder stringBuilder = new StringBuilder(); String temp; for (byte aByte : bytes) { temp = Integer.toHexString(aByte & 0xFF); if (temp.length() == 1) { //1得到一位的进行补0操作 stringBuilder.append("0"); } stringBuilder.append(temp); } return stringBuilder.toString(); } } SHA-256算法: sha256算法也是一种密码散列函数,对于任意长度的消息,SHA256都会产生一个256bit长的散列值(哈希值),用于确保信息传输完整一致,称作消息摘要。这个摘要相当于是个长度为32个字节的数组,通常用一个长度为64的十六进制字符串来表示。 和MD5算法对比 相同点: 1、都是密码散列函数,加密不可逆。 2、都可以实现对任意长度对象加密,都不能防止碰撞。 安全性方面: 1、SHA256(称SHA2)的安全性最高,但是耗时要其他两种多很多。 2、md5相对来说比较容易碰撞,安全性没这么高。 性能方面: 以个60M的件为测试样本,经过1000次的测试平均值,这两种算法的表现如下: MD5算法运1000次的平均时间为:226ms SHA256算法运1000次的平均时间为:473ms 总而言之,md5和sha256都是密码散列函数,加密不可逆。虽然都不能防止碰撞,但是相对而言,md5比较容易碰撞,安全性没有sha256高。 代码:SHA256Util package RSASha256; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class SHA256Util { public static void main(String[] args) { String sha256Str = getSha256Str("123456"); System.out.println(sha256Str); } /** * sha256加密 * * @param str 要加密的字符串 * @return 加密后的字符串 */ public static String getSha256Str(String str) { MessageDigest messageDigest; String encodeStr = ""; try { messageDigest = MessageDigest.getInstance("SHA-256"); messageDigest.update(str.getBytes(StandardCharsets.UTF_8)); encodeStr = byte2Hex(messageDigest.digest()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return encodeStr; } /** * sha256加密 将byte转为16进制 * * @param bytes 字节码 * @return 加密后的字符串 */ private static String byte2Hex(byte[] bytes) { StringBuilder stringBuilder = new StringBuilder(); String temp; for (byte aByte : bytes) { temp = Integer.toHexString(aByte & 0xFF); if (temp.length() == 1) { //1得到一位的进行补0操作 stringBuilder.append("0"); } stringBuilder.append(temp); } return stringBuilder.toString(); } } 三、国密算法介绍 简介: 为保障国家密码应用安全,2011年GJMM管理局发布《关于做好公钥密码算法升级工作的通知》, 要求自2011年3月1日起在建和拟建公钥密码基础设施电子认证系统和密钥管理系统应使用国密算法。 《金融和重要领域密码应用与创新发展工作规划(2018-2022年) 》以及相关法规文件也要求我国金融 和重要领域密码应用采用SM2国产密码算法体系。 国密算法是指GJMM管理局认定的一系列国产密码算法,包括SM1-SM9以及ZUC等。 其中SM1、SM4、SM5、SM6、SM7、SM8、ZUC等属于对称密码,SM2、SM9等属于公钥密码,SM3属于单向散列函数。 目前我国主要使用公开的SM2、SM3、SM4作为商用密码。  SM1: SM1也叫商密1号算法,是一种国产的对称算法,分组长度和密钥长度都为 128 比特,该算法不公开,调用该算法时,需要通过加密芯片的接口进行调用。算法安全保密强度及相关软硬件实现性能与 AES 相当 SM2: SM2算法和RSA算法都是公钥密码算法,SM2算法是一种更先进安全的算法,在我们国家商用密码体系中被用来替换RSA算法。 随着密码技术和计算机技术的发展,目前常用的1024位RSA算法面临严重的安全威胁,我们国家密码管理部门经过研究,决定采用SM2椭圆曲线算法替换RSA算法。 代码:SM2Util package GMSM; import org.bouncycastle.asn1.gm.GMNamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.SM2Engine; import org.bouncycastle.crypto.params.*; import org.bouncycastle.crypto.signers.SM2Signer; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Hex; import java.math.BigInteger; import java.nio.charset.Charset; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.ECGenParameterSpec; /** * SM2 工具类 * */ public class SM2Util { //SM2推荐曲线名称 static String SM2_CURVE_NAME = "sm2p256v1"; public static final Charset UTF_8 = Charset.forName("utf-8"); /** * 生成密钥 * * @return * @throws Exception */ public static KeyPair genKeyPair() throws Exception { final ECGenParameterSpec sm2Spec = new ECGenParameterSpec(SM2_CURVE_NAME); // 获取一个椭圆曲线类型的密钥对生成器 final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider()); SecureRandom random = new SecureRandom(); // 使用SM2的算法区域初始化密钥生成器 kpg.initialize(sm2Spec, random); // 获取密钥对 KeyPair keyPair = kpg.generateKeyPair(); return keyPair; } /**SM2根据公钥加密 * param: message 待加密内容 , publicKey 加密公钥(BASE64编码) * return: 加密信息的Base64编码 * @throws InvalidCipherTextException * */ public static String encryptBySM2(String message, String publicKey) throws InvalidCipherTextException { ECDomainParameters domin = getDomain(); //公钥对象 ECPublicKeyParameters pubKeyParameters = getPubKey(publicKey,domin); byte[] cipherBytes = new byte[0]; cipherBytes = encrypt(SM2Engine.Mode.C1C3C2, pubKeyParameters, message.getBytes(UTF_8)); return Base64.toBase64String(cipherBytes); } /** * SM2根据私钥解密 * param: cipherText 待解密密文 privateKey-私钥(BASE64编码) * */ public static String decryptBySM2(String cipherText, String privateKey) throws InvalidCipherTextException { BigInteger d = new BigInteger(1,Base64.decode(privateKey)); ECDomainParameters domin = getDomain(); //私钥对象 ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(d, domin); byte[] decrypt = decrypt(SM2Engine.Mode.C1C3C2, ecPrivateKeyParameters, Base64.decode(cipherText)); return new String(decrypt,UTF_8); } /** * 根据公钥字符串创建公钥对象 * * */ public static ECPublicKeyParameters getPubKey(String publicKey, ECDomainParameters domain) { ECCurve curve = domain.getCurve(); ECPoint point = curve.decodePoint(Base64.decode(publicKey)); ECPublicKeyParameters PublicKey = new ECPublicKeyParameters(point, domain); return PublicKey; } /** * @param mode 指定密文结构,旧标准的为C1C2C3,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2 * @param pubKeyParameters 公钥 * @param srcData 原文 * @return 根据mode不同,输出的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。 * @throws InvalidCipherTextException */ public static byte[] encrypt(SM2Engine.Mode mode, ECPublicKeyParameters pubKeyParameters, byte[] srcData) throws InvalidCipherTextException { SM2Engine engine = new SM2Engine(mode); ParametersWithRandom pwr = new ParametersWithRandom(pubKeyParameters, new SecureRandom()); engine.init(true, pwr); return engine.processBlock(srcData, 0, srcData.length); } /** * @param mode 指定密文结构,旧标准的为C1C2C3,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2 * @param priKeyParameters 私钥 * @param sm2Cipher 根据mode不同,需要输入的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。 * @return 原文。SM2解密返回了数据则一定是原文,因为SM2自带校验,如果密文被篡改或者密钥对不上,都是会直接报异常的。 * @throws InvalidCipherTextException */ public static byte[] decrypt(SM2Engine.Mode mode, ECPrivateKeyParameters priKeyParameters, byte[] sm2Cipher) throws InvalidCipherTextException { SM2Engine engine = new SM2Engine(mode); engine.init(false, priKeyParameters); return engine.processBlock(sm2Cipher, 0, sm2Cipher.length); } public static ECDomainParameters getDomain() { //获取一条SM2曲线参数 X9ECParameters x9ECParameters = GMNamedCurves.getByName(SM2_CURVE_NAME); //构造domain参数 ECDomainParameters domain = new ECDomainParameters( x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN(), x9ECParameters.getH() ); return domain; } /** * 私钥签名 * @param privateKey 私钥 * @param content 待签名内容 * @return */ public static String sign(String privateKey, String content) throws CryptoException, CryptoException { //待签名内容转为字节数组 byte[] message = content.getBytes(); BigInteger domainParameters = new BigInteger(1,Base64.decode(privateKey)); ECDomainParameters domin = getDomain(); //私钥对象 ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(domainParameters, domin); //创建签名实例 SM2Signer sm2Signer = new SM2Signer(); //初始化签名实例,带上ID,国密的要求,ID默认值:1234567812345678 try { sm2Signer.init(true, new ParametersWithID(new ParametersWithRandom(privateKeyParameters, SecureRandom.getInstance("SHA1PRNG")), Strings.toByteArray("1234567812345678"))); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } sm2Signer.update(message, 0, message.length); //生成签名,签名分为两部分r和s,分别对应索引0和1的数组 byte[] signBytes = sm2Signer.generateSignature(); String sign = Base64.toBase64String(signBytes); return sign; } /** * 验证签名 * @param publicKey 公钥 * @param content 待签名内容 * @param sign 签名值 * @return */ public static boolean verify(String publicKey, String content, String sign) { //待签名内容 byte[] message = content.getBytes(); byte[] signData = Base64.decode(sign); // 获取一条SM2曲线参数 X9ECParameters sm2ECParameters = GMNamedCurves.getByName(SM2_CURVE_NAME); // 构造domain参数 ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN()); //提取公钥点 ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(java.util.Base64.getDecoder().decode(publicKey)); // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04 ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters); //创建签名实例 SM2Signer sm2Signer = new SM2Signer(); ParametersWithID parametersWithID = new ParametersWithID(publicKeyParameters, Strings.toByteArray("1234567812345678")); sm2Signer.init(false, parametersWithID); sm2Signer.update(message, 0, message.length); //验证签名结果 boolean verify = sm2Signer.verifySignature(signData); return verify; } } SM3: 国产哈希算法,也叫消息摘要算法,可以用MD5作为对比理解。该算法已公开。校验结果为256位。SM3是中华人民共和国政府采用的一种密码散列函数标准,由GJMM管理局于2010年12月17日发布。相关标准为“GM/T 0004-2012 《SM3密码杂凑算法》”。 在商用密码体系中,SM3主要用于数字签名及验证、消息认证码生成及验证、随机数生成等,其算法公开。据GJMM管理局表示,其安全性及效率与SHA-256相当。 代码:SM3Util package GMSM; import org.bouncycastle.crypto.digests.SM3Digest; import org.bouncycastle.util.encoders.Hex; import java.util.Arrays; public class SM3Util { /** * 计算SM3摘要值 * * @param srcData 原文 * @return 摘要值,对于SM3算法来说是32字节 */ public static byte[] hash(byte[] srcData) { SM3Digest digest = new SM3Digest(); digest.update(srcData, 0, srcData.length); byte[] hash = new byte[digest.getDigestSize()]; digest.doFinal(hash, 0); return hash; } /** * 验证摘要 * * @param srcData 原文 * @param sm3Hash 摘要值 * @return 返回true标识验证成功,false标识验证失败 */ public static boolean verify(byte[] srcData, byte[] sm3Hash) { byte[] newHash = hash(srcData); System.out.println(Hex.toHexString(newHash)); System.out.println(Hex.toHexString(sm3Hash)); if (Arrays.equals(newHash, sm3Hash)) { return true; } else { return false; } } } SM4: 无线局域网标准的分组数据算法。对称加密,密钥长度和分组长度均为128位。对标AES 代码:SM4Util package GMSM; import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.*; public class SM4Util { static { Security.addProvider(new BouncyCastleProvider()); } public static final String ALGORITHM_NAME = "SM4"; // AES public static final String DEFAULT_KEY = "random_seed"; // 128-32位16进制;256-64位16进制 public static final int DEFAULT_KEY_SIZE = 128; public static byte[] generateKey() throws NoSuchAlgorithmException, NoSuchProviderException { return generateKey(DEFAULT_KEY, DEFAULT_KEY_SIZE); } public static byte[] generateKey(String seed) throws NoSuchAlgorithmException, NoSuchProviderException { return generateKey(seed, DEFAULT_KEY_SIZE); } public static byte[] generateKey(String seed, int keySize) throws NoSuchAlgorithmException, NoSuchProviderException { KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME); SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); if (null != seed && !"".equals(seed)) { random.setSeed(seed.getBytes()); } kg.init(keySize, random); return kg.generateKey().getEncoded(); } /** * @description 加密 */ public static byte[] encrypt(String algorithmName, byte[] key, byte[] iv, byte[] data) throws Exception { return sm4core(algorithmName, Cipher.ENCRYPT_MODE, key, iv, data); } /** * @description 解密 */ public static byte[] decrypt(String algorithmName, byte[] key, byte[] iv, byte[] data) throws Exception { return sm4core(algorithmName, Cipher.DECRYPT_MODE, key, iv, data); } private static byte[] sm4core(String algorithmName, int type, byte[] key, byte[] iv, byte[] data) throws Exception { Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME); Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME); if (algorithmName.contains("/ECB/")) { cipher.init(type, sm4Key); } else { if(iv == null ){ cipher.init(type, sm4Key); }else{ IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); cipher.init(type, sm4Key, ivParameterSpec); } } return cipher.doFinal(data); } } 四、各类支付系统常见加密方式组合介绍 在支付系统交互中,有多种多样的加密方式组合,这里就简单介绍两钟常用的。 1.传统方式 用可逆,非对称算法(rsa,sm2等)使用对方公钥对报文内的关键信息进行加密,得到的密文进行编码,然后再对密文编码后的串加盐后使用不可逆算法(sha256,md5,sm3等)进行签名,使用这些算法签名后得到的是一个16进制的串,签名放到另一个字段,一般是和加密后的信息并列的。 步骤: 1.使用对方的RSA公钥对明文进行加密,得到的密文进行base64编码,作为data 2.将data的值加上约定好的盐,使用sha256算法进行签名,得到的签名是16进制的串,放到sign 加密后的json: { "data": "auwPDINowtdseZTfcCR8B1OPsevJE1MqIVOLn56WDvw3gjksSjkpMGp4lpYiqZkJ9G2PTaYC2DZSiyhbjtLvfnJE54mZz649gTWQVIHAn5fzV8Vs3JL3Kf9WB0Br9EbR07qrfGlWCvkkgktmTfDS/4qQGywn7TVQ3EH6HwQI7kUf0GlZ9CYcYHKi4/F61s6H9Hws+CPrhchGfJmHnu+835dSVS4IJOnA+0S5JXWVEN/is5i5cmxFb3RUYZbfpu+7JLDgcXLC8oNKIZxSE1aXhoFMj4CTTYCyRxKNCD015fnBMKh+7TYHEksyyybtVjkPqDeczgT3z+Qvj9vdyUZB1Q==", "sign": "d3b36e7a837d6241445e506faf72c8d8e3467ee9c11efae0c67dfb5b15f44b6e" } 接收后,先拿到密文,加入约定好的盐进行验签,验签通过后再用私钥进行解密。返回报文同理,只不过返回报文用的也是对方的公钥加密,对方接收后用自己的私钥解密。 demo:RSASha256Test package RSASha256; import java.util.HashMap; import java.util.Map; public class RSASha256Test { public static void main(String[] args) { Map<String,String> jsonData = new HashMap<>(); String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuazymB25s/ueIfwMM2H74736dQQE4bvRDgNfWltM4PzduNC84ntav41qEgbZtby47O57m/YDVf6vMXJH25ejlsBBhls7FD7xHTkOrskZqLekH0xCs2xa/akdwllbewUNzvWMuQy8X3j2VnrILXzVjvqYxkFY0itMo8fq1xSO6B/S5/RaeKpTtIepFB2cIU9vMrtLoCsnjVoTuPdWcoC79LtS0g1Q5BV4dn+Uca+8/gUbhS7vbth3oLZt6gXTRjJrPxhT4lqWsCZqwkQHPG/8JQgnFqs7+R0KcyemG/411IaZI9WYRGTsQji8sQ2q7HAlZRXvJn+As7jJPvyi67JGKwIDAQAB"; String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC5rPKYHbmz+54h/AwzYfvjvfp1BAThu9EOA19aW0zg/N240Lzie1q/jWoSBtm1vLjs7nub9gNV/q8xckfbl6OWwEGGWzsUPvEdOQ6uyRmot6QfTEKzbFr9qR3CWVt7BQ3O9Yy5DLxfePZWesgtfNWO+pjGQVjSK0yjx+rXFI7oH9Ln9Fp4qlO0h6kUHZwhT28yu0ugKyeNWhO491ZygLv0u1LSDVDkFXh2f5Rxr7z+BRuFLu9u2Hegtm3qBdNGMms/GFPiWpawJmrCRAc8b/wlCCcWqzv5HQpzJ6Yb/jXUhpkj1ZhEZOxCOLyxDarscCVlFe8mf4CzuMk+/KLrskYrAgMBAAECggEABjC+8dVj4J1N+2IU4g2tQT2PQSF+LCx/3tC7+B49JO8pUUUcVwy3zNUhKTKzRXziSXv2ARAlslNIcgSWYrreiGMmjB00jgs/LLM/SxKHWXmt7iEzxBmjuvtNc7JY+3QCrti+9Vh4W1KEHAQB8opL8HVobIu3M2KgLoG20a7syM5gPiCUb3EQ6P30u47y7I2wsbMnMmu79rPu3Q7lSNeoB9KdVV6EA6vukL3chY1QxFh6oFZS1yNjFjxy5RxT8U5Juhr5LYRnOP8MEVptbtrh34SSm0uyckkgJ6jOsrDJqo/DJxjPxixj+BWgG5/XbNq3PFEyEAqkrTZY+FMHSufoaQKBgQDpqPxpl05mDUXCnc7I6F8LZqjnjZU1h9zwKuQe94Fs3I3siWS62sJx1pZ66P9eeIOsT0T4Ye9nDx2x04yyfUcENhydFTQ5M5M9e2q1kKdNeBUT6Xy4WcRibKozUq07mLYSn0kDbuimp/Erp2w3hl5nZu/eLKDjt7yM0otv3uPp5wKBgQDLbYC4hcvk+f1kd/xJQ2ljwF+c8kHHFrlLRipf7qTB24dcrauWXbAGbbcKXx2G+3Waswyf09NdALiWab/jUUUHJm0YGxyr0k5lHci0/eC7hD3tWTL/HRyeF3umKZEkBQypzNeymE6t0lSqjL3MXFQLqumu4h677qA9/DNh7DYhHQKBgQCfAm/bj6s7iba6jVfmozPi91bkVRaAWlgBXL7nT/nU0ncGzC0vd6WxgJ3hQORgLtU0krFV8pfP45qKpHNwGA8XD5gDUiW68500zuM8chdYgeqeJVvJvNUHQfnFeXMIRpFJNPqkCnrqxwk5cvMTCi7+YS/FW0uWDDiVAMcBN4aUawKBgCOgvAiVNk6WEfEEqqTSL6UOzjAYpbiOnEk4src2fpiNMDnlGMYvBmM51/LzEaLQa5p6fV2Ipd4GAE4nmznew+4qprSwGudk3+IJw1sfk7qDwKzPEIVpvddaWYeShB8A22TpwWVAE5eR3M459AvUp8ubVW4RoDxd4Ka6gu1Fh31pAoGAYNPKJNCrdOsQH0nkH0Ld44cH6HD+zcZtoad2eQk0T2SPnKBsIS1S9W0adreOXUv1edO/hN3P/BcTuCaax7ijTscS2f0atc/NIa6LjBnK7oUBBzib9v21L72ZVZ5st4c/H7IzbQCXfS81489a7TTHP+e1HzS/XePftO0pAkr1GJ0="; String salt = "E4bvRDg"; System.out.println("公钥为:" + publicKey); String plaintext = "{'a':'1111','b':'2222'}"; String ciphertext = null; String sign = null; try { ciphertext = RSAUtil.encrypt(plaintext,publicKey); jsonData.put("data",ciphertext); System.out.println("第一步,使用对方的RSA公钥对明文进行加密,得到的密文进行base64编码,作为data:"+jsonData.get("data")); sign = SHA256Util.getSha256Str(ciphertext+salt); jsonData.put("sign",sign); System.out.println("第二步,将data的值加上约定好的盐,使用sha256算法进行签名,得到的签名是16进制的串,放到sign:"+jsonData.get("sign")); } catch (Exception e) { System.out.println("加密失败"); throw new RuntimeException(e); } System.out.println("得到的密文和签名为:"+jsonData); System.out.println("模拟另一方收到密文和签名之后.........."); System.out.println("私钥为:" + privateKey); String ciphertext1 = jsonData.get("data"); String sign1 = jsonData.get("sign"); String deciphering1 = null; if(sign1.equals(SHA256Util.getSha256Str(ciphertext1+salt))){ System.out.println("签名验证成功"); deciphering1 = RSAUtil.decrypt(ciphertext1,privateKey); System.out.printf("数据解密成功,data值为:"+deciphering1); }else{ System.out.println("签名验证失败,解密失败"); } } } 2. 一次一密+签名身份验证 生成一个对称算法秘钥,使用该秘钥对明文进行加密,然后将该秘钥使用对方的公钥进行加密,加密后编码,然后再对明文使用自己的私钥进行签名,因为私钥只有自己有,所以用自己的私钥签名后,对方使用你提供的公钥进行验签,就可以验证你的身份。 步骤: 1. 随机生成一个SM4密钥; 2. 使用SM4密钥加密密文,得到的密文进行base64编码,作为textToDecrypt 3. 将SM4密钥进行base64编码后,再使用对方的SM2公钥对这个base64串进行加密,得到的密文进行base64编码,作为keyCiphertext; 4. 对第二步的明文使用第三方sm2私钥签名,得到的签名进行base64编码后作为signature。 加密后json: { "textToDecrypt": "cU0ymFMho3HXmVq0hwDHvZg9oLsuZT19GLBKcvHzdZ4=", "signature": "MEUCIA8nO1g705B3zzmaYv7yK8jajz9r2fKuvSPZliY8k1xBAiEA0noAEiBos3fU7RwEt81jTjwlrQL+tbQfy77VK/h/ES4=", "keyCiphertext": "BJv91ULAd6fNchtKF/+b1JW2o7il0Hjbr6erQrY96S8QrDrkE7Y6EpgzM/eY1OumNsr6VbRuK0wx6yo8fq3QapYHZU5X1LzjgyMC+AiCnhzut5V3xxz4Yd0M1Zx2D4ljaOnJTDED4nS5kU2+C5VFF81DyzVDmWw6ZQ==" } 对方接收后,按照以上步骤再进行验签,解密等操作。 demo:GMSMTest package GMSM; import java.util.HashMap; import java.util.Map; import java.util.Base64; public class GMSMTest { public static void main(String[] args) throws Exception { // 你这边拿到的sm2公私钥,对方的公钥和自己的私钥 String priK = "AMz5m/WOgpgYiXnlckC7+zdvZXnQYMBcWXt1n7khfdVO"; String otherPubK = "BJkVGU87s7xhLrFCI8MGPzp2X+lizte+2So52CQ1tVEDDqOe+Q4vdQXglV2JtIOZeWc/bE8Rzdo29svmTj8+7mA="; String messge = "{'aaa':'111','bbb':'222'}"; Map<String, String> jsonData = new HashMap<>(); byte[] keyB = SM4Util.generateKey(null); String key = Base64.getEncoder().encodeToString(keyB); String algorithmName = ("SM4/ECB/PKCS5PADDING"); System.out.println("第一步,随机生成一个SM4秘钥:"+key); System.out.println("选定算法:"+algorithmName); // 用随机生成的SM4秘钥加密 byte[] encryptMSG = SM4Util.encrypt(algorithmName, keyB, null, messge.getBytes()); jsonData.put("textToDecrypt",Base64.getEncoder().encodeToString(encryptMSG)); System.out.println("第二步,使用SM4密钥加密明文,得到的密文进行base64编码,作为textToDecrypt :"+jsonData.get("textToDecrypt")); // 将随机生成的SM4秘钥,使用对方的SM2公钥进行加密 String keyCiphertext = SM2Util.encryptBySM2(key, otherPubK); jsonData.put("keyCiphertext",keyCiphertext); System.out.println("第三步,将SM4密钥进行base64编码后,再使用对方的SM2公钥对这个base64串进行加密,得到的密文进行base64编码,作为 keyCiphertext;"+jsonData.get("keyCiphertext")); // 对明文使用自己的sm2私钥进行签名 如果有需要,可以加点盐 String signature = SM2Util.sign(priK,messge); jsonData.put("signature",signature); System.out.println("第四步,对第二步的明文使用对方sm2私钥签名,得到的签名进行base64编码后作为 signature:"+jsonData.get("signature")); System.out.println("完整json串:"+jsonData); // 模拟接到json传之后的处理 // 对方拿到的,你的公钥,和对方自己的私钥 String PubK = "BNTFr3oo1xm3biGoQQnk20QJfbFvK4HNFgs1DkZJuUT9pW8I7K0D9L9P4mW+tQhWwfJlD/Nsx3BY+oriF1B25bA="; String otherPriK = "I0sHzcDm+oS2FIAHFLxxkWJi1AbrHRxEYs1AxpZTlNw="; // 将sm4秘钥进行解密 String keyCiphertext1 = SM2Util.decryptBySM2(jsonData.get("keyCiphertext"), otherPriK); System.out.println("对方使用sm2私钥解密sm4秘钥:"+keyCiphertext1); // 通过sm4秘钥解密数据密文 byte[] textToDecrypts = SM4Util.decrypt(algorithmName, Base64.getDecoder().decode(keyCiphertext1), null, Base64.getDecoder().decode(jsonData.get("textToDecrypt").getBytes())); String textToDecryptsS = new String(textToDecrypts); System.out.println("对方使用sm4秘钥解密后的数据:"+ textToDecryptsS); // 验证签名 boolean proving = SM2Util.verify(PubK, textToDecryptsS, jsonData.get("signature")); System.out.println("验证签名是否通过:"+proving); } } 国密依赖pom <dependencies> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.1-jre</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.70</version> </dependency> </dependencies> 

优秀的个人博客,低调大师

用OmniTool.Java 高效开发Omni/USDT对接应用

OmniTool.Java开发包适用于为Java应用快速增加对Omni/USDT数字资产的支持能力,即支持使用自有Omni节点的应用场景,也支持基于第三方API服务和离线裸交易的轻量级部署场景。官方下载地址:http://sc.hubwiz.com/codebag/omni-java-lib/。 1、开发包概述 OmniTool.Java开发包主要包含以下特性: 完善的Bitcoin/Omni Layer RPC API封装 支持利用自有节点或第三方服务获取指定地址的比特币utxo集合 支持离线生成Omni代币或比特币转账裸交易 支持利用自有节点或第三方服务广播裸交易 OmniTool.Java支持本地部署的Omnicored节点,也支持第三方服务提供的开放API,要增加新的第三方服务也非常简单,只需要参考代码实现如下接口: IUtxoCollector:Utxo采集器 IBroadcaster:裸交易广播器 OmniTool.Java软件包当前版本1.0.0,主要类/接口及关系如下图所示: OmniTool.Java软件包主要代码文件清单如下: 代码文件 说明 omnitool/build.gradle OmniTool.Java开发库项目构建文件 omnitool/src/main/java/omnitool/RpcClient.java OmniLayer/Bitcoin RPC API客户端实现类 omnitool/src/main/java/omnitool/RpcRequest.java RPC API请求类 omnitool/src/main/java/omnitool/RpcResponse.java RPC API响应类 omnitool/src/main/java/omnitool/ToolKit.java 开发库入口类 omnitool/src/main/java/omnitool/KeyStore.java 密钥存储接口 omnitool/src/main/java/omnitool/UtxoCollector.java UTXO采集器接口 omnitool/src/main/java/omnitool/UtxoSelector.java UTXO选择器接口 omnitool/src/main/java/omnitool/Broadcaster.java 裸交易广播器接口 omnitool/src/main/java/omnitool/KeyStoreMemory.java 内存密钥存储库实现类 omnitool/src/main/java/omnitool/KeyStoreSql.java SQL数据库密钥存储库实现类 omnitool/src/main/java/omnitool/UtxoCollectorRpc.java 基于RPC API的UTXO采集器实现类 omnitool/src/main/java/omnitool/UtxoCollectorSmartbit.java 基于云端第三方API的UTXO采集器实现类 omnitool/src/main/java/omnitool/UtxoSelectorDefault.java UTXO选择器接口的默认策略实现类 omnitool/src/main/java/omnitool/BroadcasterRpc.java 基于RPC API的裸交易广播器实现类 omnitool/src/main/java/omnitool/BroadcasterSmartbit.java 基于云端第三方API的裸交易广播器实现类 omnitool/src/main/java/omnitool/UtxoBag.java UTXO集合类 omnitool/src/main/java/omnitool/Utxo.java UTXO模型类 omnitool/src/main/java/omnitool/KeyStoreItem.java 密钥存储记录类 omnitool/src/main/java/omnitool/Utils.java 辅助工具类 demo/build.gradle OmniTool.Java演示应用项目文件 demo/src/App.java 演示应用入口代码 demo/src/RpcClientDemo.java Omni/Btc RPC API客户端使用方法演示代码 demo/src/KeyStoreMemoryDemo.java 内存密钥库使用方法演示代码 demo/src/KeyStoreSqlDemo.java Sql密钥库使用方法演示代码 demo/src/BtcTxRpcDemo.java 比特币转账交易演示代码,基于RPC API demo/src/BtcTxCloudDemo.java 比特币转账交易演示代码,基于第三方服务API demo/src/OmniTxRpcDemo.java Omni/USDT代币转账交易演示代码,基于RPC API demo/src/OmniTxCloudDemo.java Omni/USDT代币转账交易演示代码,基于第三方服务API demo/resources/log4j.properties 演示项目日志配置文件 build.gradle 根项目构建文件 settings.gradle 根项目配置文件 2、RpcClient类使用说明 RpcClient类封装了比特币以及Omni Layer的RPC API接口协议。创建RpcClient对象时,需要传入包含有效身份信息的节点RPC URL。例如,假设安装在本机的omnicored节点软件接入主网,其配置如下: rpcuser:user rpcpassword:123456 rpcport:8332 那么可以使用如下的代码来实例化RpcClient: import omnitool.RpcClient; RpcClient client = new RpcClient( "http://user:123456@127.0.0.1:8332" /*节点RPC API的URL*/ ); 使用RpcClient的call()方法可以调用Bitcoin层和omni层的所有RPC API。例如,使用listunspent调用来获取本地节点中指定地址的utxo: //import java.util.Map; Map[] unspents = client.call( Map[].class, /*返回结果类型*/ "listunspent", /*RPC API名称*/ 6, /*最小确认数*/ 999999, /*最大确认数*/ new String[]{"mgnucj8nYqdrPFh2JfZSB1NmUThUGnmsqe"} /*地址清单*/ ); for(Object unspent: unspents) { System.out.printf("txid: %s\n",(String)unspent.get("txid")); } call()方法的返回结果对应于RPC API的JSON响应中的result字段,其类型取决于我们传入的第一个参数。 call()方法的第一个参数声明方法返回的结果类型的Class对象,方法会将RPC API的JSON响应中的result字段解码为该参数指定的类型。通常我们都可以使用Map或Map[]来对应JSON响应中的result字段的内容,例如上例所示。这种处理方式可以适应不断变化中的RPC API,但从结果中提取数据时,不得不小心处理类型转换的问题。 call()方法的第二个参数声明要调用的RPC API方法名,从第三个参数开始的其他参数则表示所指定的RPC API方法的参数。 2.1 定义自己的结果类 可选地,也可以自己定义一个类来简化从call()方法的返回结果中提取数据的难度。例如,对于上面的示例,我们可以定义一个Unspent类来描述listupsent响应中的JSON对象(不需要定义所有的字段,按自己的需求选择): class Unspent{ public String txid; public long vout; public String account; public String scriptPubKey; public double amount; public long confirmations; } 那么我们可以按如下的方式调用RpcClient: Unspent[] unspents = client.call( Unspent[].class, /*返回结果类型*/ "listunspent", /*RPC API方法名*/ 6, /*最小确认数*/ 999999, /*最大确认数*/ new String[]{"mgnucj8nYqdrPFh2JfZSB1NmUThUGnmsqe"} /*地址清单*/ ); for(Object unspent: unspents) { System.out.printf("txid: %s\n",unspent.txid); System.out.printf("vout: %d\n",unspent.vout); System.out.printf("amount:%f\n",unspent.amount); } 显然,定义自己的结果类可以将RPC API的JSON响应直接反序列化到指定的类型,对于操作复杂响应结果会很有帮助。但比特币和Omni层的RPC API不仅在动态演化中,而且有些JSON响应的结构本身就是动态的,因此往往还需要结合使用前面更通用的Map或Map[]类型。 2.2 Omni层RPC API OmniCore节点在比特币原有的RPC接口之外,扩充了额外的接口用来操作Omni层的数据,这些扩展的RPC接口采用omni_前缀以区隔于Bitcoin的原有RPC接口。 例如,获取某个地址的USDT代币余额需要使用Omni层的omni_getbalance调用,下面的代码获取地址1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P的USDT(资产ID:31)余额: Map[] balances = client.call( Map[].class, /*返回结果类型*/ "omni_getbalance" /*Omni RPC API方法名*/ "1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P", /*账户地址*/ 31 /*Omni资产ID:USDT=31*/ ); for(Map b:balances){ System.out.printf("balance: %s\n",(String)b.balance); } 类似的,可以使用omni_send调用来执行简单的USDT转账。例如,下面的代码从地址3M9qvHKtgARhqcMtM5cRT9VaiDJ5PSfQGY向地址37FaKponF7zqoMLUjEiko25pDiuVH5YLEa转入100.0个USDT代币: String txid = client.call( String.class, /*返回结果类型*/ "omni_send", /*RPC API方法名*/ "3M9qvHKtgARhqcMtM5cRT9VaiDJ5PSfQGY", /*代币转出地址*/ "37FaKponF7zqoMLUjEiko25pDiuVH5YLEa", /*代币转入地址*/ 31, /*代币ID:USDT*/ "100.00" /*转移的代币数量*/ ); System.out.printf("tx hash => %s\n",txid); 开发包中的demo/RpcClientDemo.java示例代码使用RpcClient完整演示了在Omni层的代币发行与转账功能,如果你计划搭建自己的Omni Core节点,相信这个示例会有很大帮助。 3、ToolKit类使用说明 如果不愿意搭建自己的Omni Core节点,而是希望基于第三方API为自己的Java应用增加对Omni Layer/USDT的支持,那么最简单的方法是使用离线交易的入口类ToolKit。 ToolKit类的主要作用是创建并广播Omni代币或比特币转账裸交易,它的基本使用步骤如下: 创建一个ToolKit实例 使用AddKey()方法将必要的私钥加入该ToolKit实例,例如转出地址的私钥,因为ToolKit需要利用私钥对裸交易进行签名 使用SendOmnicoin()方法生成并广播Omni代币转账裸交易,或者使用SendBitcoin()方法生成并广播比特币转账裸交易 3.1 Omni/USDT代币转账 使用ToolKit实现的Omni/USDT代币转账示例代码如下,说明见注释: import omnitool.*; String network = "main"; ToolKit kit = new ToolKit( network, /*接入的网络*/ new KeyStoreMemory(), /*使用内存密钥库*/ new UtxoCollectorSmartbit(network), /*使用云端Utxo采集器*/ new UtxoSelectorDefault(), /*使用默认策略Utxo选择器*/ new BroadcasterSmartbit(network) /*使用云端裸交易广播器*/ ); String privHex = "4aec8e45106....00d5c5a05b"; /*私钥:16进制字符串*/ kit.addKey(privHex); /*将私钥加入ToolKit*/ String from = kit.getKeyStore() .getByKey(privHex).address; /*私钥对应的地址作为发起账号*/ String to = "1GxX5tQR1C.....x2zbdj4mMuDcWR"; /*接收地址*/ String txid = kit.sendOmnicoin( from, /*发送方地址,私钥必须已经加入钱包*/ to, /*接收方地址*/ 31, /*转账代币ID,USDT=31*/ 10000 /*转账代币数量,调整为最小单位计量的整数*/ null, /*比特币手续费支付地址,私钥必须已加入ToolKit*/ 546, /*向接收方发送的流通比特币,单位:satoshi*/ 1000, /*交易手续费,单位:satoshi*/ true /*是否广播*/ ); System.out.printf("txid => %s\n",txid); /*打印交易哈希*/ 注意: ToolKit实例利用钱包中的私钥生成地址列表,并利用这些地址从第三方服务获取utxo信息。 因此需要钱包中 的私钥对应地址在链上有utxo存在,ToolKit对象才能够成功构造并签名裸交易。 转账目标地址应当与创建Toolkit对象时指定的网络一致,例如主网的p2pkh地址,前缀应当为1。 3.2 指定Omni交易的手续费支付地址 在Omni协议层不需要支付交易手续费,但是Omni交易所嵌入的比特币交易依然需要支付手续费。当sendOmnicoin()方法的手续费支付地址设置为null时,将使用发送方地址支付比特币交易手续费。当你的Java应用需要实现多账户归集功能时,使用统一的手续费支付地址会更容易管理一些。 例如,下面的代码使用地址35stX1w6LKHj7hGHz6GVNzXZCdUhAeqDb6支付Omni交易的手续费: String txid = kit.sendOmnicoin( from, /*发送方地址,私钥必须已加入ToolKit*/ to, /*接收方地址*/ 31, /*转账OMNI代币ID,31:USDT*/ 10000, /*转账OMNI代币数量,已调整至最小单位*/ "35stX1w6LKH...CdUhAeqDb6" /*交易手续费支付地址,私钥必须已加入ToolKit*/ 546, /*向接收方发送的流通比特币,单位:satoshi*/ 1000, /*交易手续费,单位:satoshi*/ true /*是否广播*/ ); 注意: 即使指定了余额充足的手续费支付地址,Omni交易的发送方依然必须有微量的比特币 余额(546 SATOSHI),因为Omni协议需要交易发送方至少有一个可用UTXO。 手续费支付地址同时也是找零地址,多余的比特币将返回至该地址 3.3 指定Omni交易的比特币转账数量 由于Omni交易要求发送方必须有可用的UTXO,因此为了便于接收Omni代币的地址可以继续流通所持有的Omni代币,sendOmnicoin()方法需要至少向接收方地址转入546 SATOSHI的比特币,可以在调用该方法时修改这个默认数值。 例如,下面的代码转入接收方1000个SATOSHI: String txid = kit.SendOmnicoin( from, /*发送方地址,私钥必须已加入ToolKit*/ to, /*接收方地址*/ 31, /*转账OMNI代币ID,31:USDT*/ 10000, /*转账OMNI代币数量,已调整至最小单位*/ fundAddr: "35stX1w6LKH...CdUhAeqDb6" /*交易手续费支付地址,私钥必须已加入ToolKit*/ 1000, /*向接收方发送的流通比特币,单位:satoshi*/ 1000, /*交易手续费,单位:satoshi*/ true /*是否广播*/ ); 3.4 指定Omni交易的手续费 sendOmnicoin()方法可以设置交易手续费,例如设置为3000 SATOSHI: String txid = kit.SendOmnicoin( from, /*发送方地址,私钥必须已加入ToolKit*/ to, /*接收方地址*/ 31, /*转账OMNI代币ID,31:USDT*/ 10000, /*转账OMNI代币数量*/ fundAddr: "35stX1w6LKH...CdUhAeqDb6" /*交易手续费支付地址,私钥必须已加入ToolKit*/ 1000, /*向接收方发送的流通比特币,单位:satoshi*/ 3000, /*交易手续费,单位:SATOSHI*/ true /*是否广播*/ ); 3.5 仅生成Omni裸交易但不广播 有时可能只需要生成Omni转账裸交易但并不需要广播出去,可以将sendOmnicoin()方法的最后一个参数设置为false来取消广播,这时将返回生成的裸交易。例如: String rawtx = kit.SendOmnicoin( from, /*发送方地址,私钥必须已加入ToolKit*/ to, /*接收方地址*/ 31, /*转账OMNI代币ID,31:USDT*/ 10000, /*转账OMNI代币数量,已调整至最小单位*/ fundAddr: "35stX1w6LKH...CdUhAeqDb6" /*交易手续费支付地址,私钥必须已加入ToolKit*/ 1000, /*向接收方发送的流通比特币,单位:satoshi*/ 3000, /*交易手续费,单位:SATOSHI*/ false /*是否广播*/ ); System.out.println(rawtx); /*打印裸交易内容*/ 3.6 比特币转账 OmniTool.Java也支持比特币转账裸交易的生成与广播。 例如,下面的代码从ToolKit的某个地址向其他地址转10000 SATOSHI: String privHex = "4aec8e45106....00d5c5a05b"; /*私钥:16进制字符串*/ kit.addKey(privHex); /*将私钥加入ToolKit*/ String from = kit.getKeyStore() .getByKey(privHex).address; /*私钥对应的地址作为发起账号*/ String to = "1GxX5tQR1C.....x2zbdj4mMuDcWR"; /*接收地址*/ String txid = kit.sendBitcoin( from, /*发送方地址*/ to, /*接收方地址*/ 10000, /*转账比特币数量,单位:SATOSHI*/ 1500, /*手续费,单位:SATOSHI*/ null, /*找零地址*/ true /*是否广播*/ ); 当找零地址设置为null时,SendBitcoin()方法使用发送方地址作为找零地址。下面的代码创建一个新地址接收找零: String changeAddr = kit.newAddress(); /*创建新地址*/ String txid = kit.sendBitcoin( from, /*发送方地址*/ to, /*接收方地址*/ 10000, /*转账比特币数量,单位:SATOSHI*/ 1500, /*手续费,单位:SATOSHI*/ changeAddr, /*找零地址*/ true /*是否广播*/ ); 类似的,当只需要生成裸交易而不希望广播时,可以设置最后一个参数为false。 4、UTXO采集器 OmniTool.Java使用接口UtxoCollector来约定UTXO的采集功能。该接口的实现需要支持获取指定地址的候选UTXO集合,可指定多个地址。 接口方法: UtxoBag collect(String[] addresses); /*提取并返回候选UTXO集合*/ 参数addresses用来声明要收集UTXO的地址清单。 当前实现类: UtxoCollectorSmartbit:基于云端第三方API实现的Utxo采集器 UtxoCollectorRpc:基于omnicored节点RPC API实现的Utxo采集器 例如,下面的代码使用UtxoCollectorSmartbit获取测试链某个指定地址的UTXO: UtxoCollector collector = new UtxoCollectorSmartbit( "main" /*主链*/ ); String[] addresses = new String[]{"1C3TZ...brS2xHM"}; UtxoBag collected = collector.Collect( addresses /*地址清单*/ ); 5、UTXO选择器 OmniTool.Java使用接口UtxoSelector来约定UTXO的筛选策略。该接口的实现需要根据目标金额从候选UTXO中选择可用UTXO,并返回新的UtxoBag实例。 接口方法: UtxoBag select(long target,UtxoBag collected); /*选择可消费UTXO,返回UtxoBag对象*/ 参数target声明要达成的最低金额目标,单位:SATOSHI。 参数collected是候选的utxo集合,通常是UtxoCollector的collect()调用返回的结果。 当前实现类: UtxoSelectorDefault:选择不少于6个确认的未消费UTXO 例如下面的代码使用UtxoSelectorDefault实例从候选UTXO中删选出至少100000 SATOSHI的UTXO: //collected表示候选UTXO集合,来自Utxo采集器的collect()调用结果 UtxoSelector selector = new UtxoSelectorDefault(); UtxoBag selected = selector.select( 100000, /*最低目标金额*/ collected /*候选UTXO集合*/ ); System.out.printf("total:%d\n":selected.getTotal()); /*打印输出选中utxo总额*/ 考虑到UTXO的不可分割性,筛选出的若干UTXO的总和,有可能超过目标金额。可以使用UtxoBag实例的getTotal()方法查看集合中的UTXO总额,如上。 6、裸交易广播器 OmniTool.Java使用Broadcaster接口约定裸交易广播的功能规格。该接口的实现应当将裸交易广播到Omni/Btc网络中。 接口方法: String broadcast(String rawtx); /*广播裸交易*/ 参数rawtx用来声明要广播的裸交易,类型为16进制字符串。 当前实现类: BroadcasterSmartbit BroadcasterRpc 例如,下面的代码使用BroadcasterSmartbit将裸交易码流广播到Omni/Btc网络中: Broadcaster broadcaster = new BroadcasterSmartbit( "testnet" /*测试链*/ ); String txid = broadcaster.broadcast( "01000000011da9283b4...59f58488ac00000000" /*裸交易*/ ); 7、密钥存储接口 OmniTool.Java使用KeyStore约定密钥存储的功能规格。 接口方法: bool add(KeyStoreItem item); /*存入密钥*/ KeyStoreItem[] list(); /*浏览全部密钥*/ KeyStoreItem getByKey(); /*查询指定16进制私钥对应的密钥信息*/ KeyStoreItem getByWif(); /*查询指定WIF格式私钥对应的密钥信息*/ KeyStoreItem getByAddress(); /*查询指定地址对应的密钥信息*/ KeyStoreItem getByScript(); /*查询指定公钥脚本对应的密钥信息*/ KeyStore当前实现类有两个: KeyStoreMemory:基于内存字典实现,没有持久化能力,适合调试 KeyStoreSql:基于Sql数据库实现,适合作为生产环境密钥存储的参考实现 密钥存储实例的主要功能就是为ToolKit提供密钥存储和查询能力。下面的代码使用KeyStoreSql来启动ToolKit,生成几个不同类型的地址,导入16进制私钥和WIF私钥,然后进行查询: ToolKit kit = new ToolKit( "testnet", new KeyStoreSqlite("testnet.wallet"), null,null,null ); String addr1 = kit.newAddress("SEGWIT-P2SH"); /*生成隔离见证p2sh地址*/ String addr2 = kit.newAddress("SEGWIT"); /*生成隔离见证地址*/ String addr3 = kit.newAddress("P2PKH"); /*生成P2PKH地址,默认选项*/ String addr4 = kit.addKey( /*导入16进制私钥*/ "4aec8e45106....00d5c5a05b", "SEGWIT-P2SH" /*使用该私钥的SEGWIT-P2SH地址*/ ); String addr5 = kit.addWif( /*导入WIF格式的私钥*/ "cNJFgo1driF...SkdcF6JXXwHMm" ); /*默认使用私钥的P2PKH地址*/ KeyStoreItem[] items = kit.list(); /*返回全部密钥记录*/ for(KeyStoreItem item:items) { System.out.printf("key => %s\n",item.key); System.out.printf("wif => %s\n",item.wif); System.out.printf("address => %s\n",item.address); System.out.printf("script => %s\n",item.script); } KeyStoreItem item = kit.getByAddress(addr1); /*查询指定地址的密钥记录*/ System.out.printf("key => %s\n",item.key); 下载地址:Omni/USDT Java开发包 - 汇智网

优秀的个人博客,低调大师

AnalyticDB for PG 如何作为数据源对接帆软 FineBI

AnalyticDB for PostgreSQL 基于开源数据库 Greenplum 构建,兼容Greenplum 和 PostgreSQL 的语法,接口和生态。本章节介绍如何通过FineBI连接 分析型数据库PostgreSQL版 并进行报表开发。 准备工作开始使用FineBI之前,用户需要先完成以下准备工作。下载并安装FineBI 操作步骤首先进行”新建数据连接“,并选择 "Greenplum Database"。 之后将 JDBC URL,数据库名称,用户名密码等输入进行连接测试。 注意事项对于新安装的FineBI,第一次连接 Greenplum 或 PostgreSQL 数据源时,需要先下载其 JDBC Driver,可以按操作步骤下载并将对应JDBC 驱动安装到 FineBI 目录。AnalyticDB for Postgre

优秀的个人博客,低调大师

[雪峰磁针石博客]python与java对接工具Jython介绍

快速入门 下面我们使用jython来调用自定义jar包中的类。 编辑java文件:Beach.java public class Beach { private String name; private String city; public Beach(String name, String city){ this.name = name; this.city = city; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } } 编译成jar包: # javac Beach.java # echo Main-Class: Beach >manifest.txt # jar cvfm Craps.jar manifest.txt *.class 已添加清单 正在添加: Beach.class(输入 = 795) (输出 = 430)(压缩了 45%) 正在添加: Point.class(输入 = 708) (输出 = 445)(压缩了 37%) 添加Craps.jar到CLASSPATH,修改/etc/profile,修改CLASSPATH export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:/usr/local/jython/Craps.jar 使用. /etc/profile导入变量。然后使用jython调用java代码。 # ./jython Jython 2.7.0 (default:9987c746f838, Apr 29 2015, 02:25:11) [Java HotSpot(TM) 64-Bit Server VM (Oracle Corporation)] on java1.7.0_79 Type "help", "copyright", "credits" or "license" for more information. >>> import Beach >>> beach = Beach("Cocoa Beach","Cocoa Beach") >>> beach.getName() u'Cocoa Beach' >>> print beach.getName() Cocoa Beach >>> 基础 Linux安装: # java -jar jython-installer-2.7.0.jar # ./jython Jython 2.7.0 (default:9987c746f838, Apr 29 2015, 02:25:11) [Java HotSpot(TM) 64-Bit Server VM (Oracle Corporation)] on java1.7.0_79 Type "help", "copyright", "credits" or "license" for more information. >>> windows安装:双击jython-installer-2.7.0.jar, 执行jython.exe 变量定义: java int x = 0; jython: x = 0 x = 'Hello Jython' 是动态类型的。 保留字: and assert break class continue def del elif else except exec finally for from global or pass print raise return try while with yield 编码结构使用缩进。 函数名也可以作为函数的参数。 语句关键字: if-elif-else for while continue break try-except-finally assert def print del raise import 文件名和类名一样可以直接导入。 此部分参考资料: http://www.jython.org/jythonbook/en/1.0/JythonAndJavaIntegration.html http://www.skylit.com/javamethods/faqs/createjar.html 参考资料: http://www.jython.org/jythonbook/en/1.0/ 模块和包 查看命名空间 >>> dir() ['__builtins__', '__doc__', '__name__', '__package__'] Jython的导入不像java那样必须在头部。导入java以右边名优先,python以左边名优先。模块查找路径: >>> import sys >>> sys.path ['', '/usr/local/jython2.7.0/Lib', '__classpath__', '__pyclasspath__/', '/usr/local/jython2.7.0/Lib/site-packages'] >>> import sys >>> sys.path ['', '/usr/local/jython2.7.0/Lib', '__classpath__', '__pyclasspath__/', '/usr/local/jython2.7.0/Lib/site-packages'] >>> import sys >>> sys.path.append("/home/andrew/code/java/mysql-connector-java-5.1.39/mysql-connector-java-5.1.39-bin.jar") >>> import com.mysql *sys-package-mgr*: can't write cache file for '/home/andrew/code/java/mysql-connector-java-5.1.39/mysql-connector-java-5.1.39-bin.jar' *sys-package-mgr*: can't write index file >>> dir(com.mysql) ['__name__', 'fabric', 'jdbc'] >>> import java.util.zip >>> dir(java.util.zip) ['Adler32', 'CRC32', 'CheckedInputStream', 'CheckedOutputStream', 'Checksum', 'DataFormatException', 'Deflater', 'DeflaterInputStream', 'DeflaterOutputStream', 'GZIPInputStream', 'GZIPOutputStream', 'Inflater', 'InflaterInputStream', 'InflaterOutputStream', 'ZipEntry', 'ZipError', 'ZipException', 'ZipFile', 'ZipInputStream', 'ZipOutputStream', '__name__'] >>> dir(java.util.zip.ZipInputStream) ['__class__', '__copy__', '__deepcopy__', '__delattr__', '__doc__', '__ensure_finalizer__', '__eq__', '__format__', '__getattribute__', '__hash__', '__init__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__subclasshook__', '__unicode__', 'available', 'class', 'close', 'closeEntry', 'equals', 'getClass', 'getNextEntry', 'hashCode', 'mark', 'markSupported', 'nextEntry', 'notify', 'notifyAll', 'read', 'reset', 'skip', 'toString', 'wait'] Jython 查找Jar和类的方法: python.packages.paths python.packages.directories java.class.path java.ext.dirs python.cachedir.skip pycimport把python字节码转换成java字节码。Jython导入java包可能有多个包合并。 >>> import sys >>> sys.path_hooks [<type 'org.python.core.JavaImporter'>, <type 'zipimport.zipimporter'>, <type 'ClasspathPyImporter'>] import pycimport 数据类型 Jython特定的collection,多用于给java传递数据。 # Import and use a Java ArrayList >>> import java.util.ArrayList as ArrayList >>> arr = ArrayList() # Add method will add an element to the list and return a boolean to signify successsful addition >>> arr.add(1) True >>> arr.add(2) True >>> print arr [1, 2] 比较老的Jarray,不推荐使用,不过可以创建空数组: 类型: Character Java Equivalent z boolean b byte c char d Double f Float h Short i Int l Long >>> my_seq = (1,2,3,4,5) >>> from jarray import array >>> array(my_seq,'i') array('i', [1, 2, 3, 4, 5]) >>> myStr = "Hello Jython" >>> array(myStr,'c') array('c', 'Hello Jython') >>> arr = zeros(10,'z') >>> arr array('z', [False, False, False, False, False, False, False, False, False, False]) >>> arr2 = zeros(6, 'i') >>> arr2 array('i', [0, 0, 0, 0, 0, 0]) 求素数 `>>> ``nums = range(2, 50) ` `>>> ``for i in range(2, 8): ` `... `` nums = filter(lambda x: x == i or x % i, nums)` `... ` `>>> ``print nums` 读取输入: <pre># Obtain a value from the command line and store it into a variable >>> import sys >>> fav_team = sys.stdin.readline() Cubs >>> sys.stdout.write("My favorite team is: %s" % fav_team) My favorite team is: Cubs</pre> 函数 协程: **def** search_file(filename): **print** 'Searching file *%s*' % (filename) my_file = open(filename, 'r') file_content = my_file.read() my_file.close() **while** True: search_text = (**yield**) search_result = file_content.count(search_text) **print** 'Number of matches: *%d*' % (search_result)</pre> Jython脚本编程基础 os.chdir不会改变jvm的启动目录。 >>> import os >>> os.getcwd() '/Users/frank/Desktop/frank/hg/jythonbook~jython-book/src/chapter8' >>> from java.io import File >>> f = File(".") >>> for x in f.list(): ... print x ... args.py search.py >>> os.chdir("/Users/frank") >>> os.getcwd() '/Users/frank' >>> os.listdir(".") ['Desktop', 'Documents', 'Downloads', 'Dropbox', 'Library', 'Movies', 'Music', 'Pictures', 'Public', 'Sites'] >>> g = File(".") >>> for x in g.list(): ... print x ... args.py search.py 编译代码 !/usr/local/jython2.7.0/bin/jython from javax.tools import (ForwardingJavaFileManager, ToolProvider, DiagnosticCollector,)names = ["/home/andrew/code/python/jythonbook/src/chapter8/HelloWorld.java"]compiler = ToolProvider.getSystemJavaCompiler()diagnostics = DiagnosticCollector()manager = compiler.getStandardFileManager(diagnostics, None, None)units = manager.getJavaFileObjectsFromStrings(names)comp_task = compiler.getTask(None, manager, diagnostics, None, None, units)success = comp_task.call()manager.close() import osimport sysimport glob from javax.tools import (forwardingjavafilemanager, toolprovider, diagnosticcollector,) tasks = {} def task(func): tasks[func.func_name] = func @taskdef clean(): files = glob.glob("\*.class") **for** file **in** files: os.unlink(file) @taskdef compile(): files = glob.glob("\*.java") _log("compiling *%s*" % files) **if** **not** _compile(files): quit() _log("compiled") def _log(message): **if** options.verbose: **print** message def _compile(names): compiler = toolprovider.getsystemjavacompiler() diagnostics = diagnosticcollector() manager = compiler.getstandardfilemanager(diagnostics, none, none) units = manager.getjavafileobjectsfromstrings(names) comp_task = compiler.gettask(none, manager, diagnostics, none, none, units) success = comp_task.call() manager.close() **return** success if name == '__main__': **from** **optparse** **import** optionparser parser = optionparser() parser.add_option("-q", "--quiet", action="store_false", dest="verbose", default=true, help="don't print out task messages.") parser.add_option("-p", "--projecthelp", action="store_true", dest="projecthelp", help="print out list of tasks.") (options, args) = parser.parse_args() **if** options.projecthelp: **for** task **in** tasks: **print** task sys.exit(0) **if** len(args) < 1: **print** "usage: jython builder.py [options] task" sys.exit(1) **try**: current = tasks[args[0]] **except** KeyError: **print** "task *%s* not defined." % args[0] sys.exit(1) current()</pre> public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World"); } } [frank@pacman chapter8]$ jython builder.py --helpUsage: builder.py [options] Options: -h, --help show this help message and exit -q, --quiet Don't print out task messages. -p, --projecthelp Print out list of tasks. [frank@pacman chapter8]$ jython builder.py --projecthelpcompileclean[frank@pacman chapter8]$ jython builder.py compilecompiling ['HelloWorld.java']compiled[frank@pacman chapter8]$ lsHelloWorld.java HelloWorld.class builder.py[frank@pacman chapter8]$ jython builder.py clean[frank@pacman chapter8]$ lsHelloWorld.java builder.py[frank@pacman chapter8]$ jython builder.py --quiet compile[frank@pacman chapter8]$ lsHelloWorld.class HelloWorld.java builder.py[frank@pacman chapter8]$

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册