jjzjj

sm2证书生成(openssl生成公私钥对)—使用

csj50 2024-04-05 原文

用上一篇的命令生成公私钥对。

1、sm2PubKey.pem

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEmxmSy4HOD2d2sakaJTw0QFhRGZs2
5umcKzmg12FAsYNjVRmtLxcbydzTMELGKpHHle//IZ0Eqx7P15IKiyoK/g==
-----END PUBLIC KEY-----

2、sm2PriKeyPkcs8.pem

-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgilIj1dWX+977jcQO
oHDQq6XtbWaazcZdJiHjXpAhKD6hRANCAASbGZLLgc4PZ3axqRolPDRAWFEZmzbm
6ZwrOaDXYUCxg2NVGa0vFxvJ3NMwQsYqkceV7/8hnQSrHs/XkgqLKgr+
-----END PRIVATE KEY-----

3、openssl ec -in sm2PriKeyPkcs8.pem -text

read EC key
Private-Key: (256 bit)
priv:
    8a:52:23:d5:d5:97:fb:de:fb:8d:c4:0e:a0:70:d0:
    ab:a5:ed:6d:66:9a:cd:c6:5d:26:21:e3:5e:90:21:
    28:3e
pub:
    04:9b:19:92:cb:81:ce:0f:67:76:b1:a9:1a:25:3c:
    34:40:58:51:19:9b:36:e6:e9:9c:2b:39:a0:d7:61:
    40:b1:83:63:55:19:ad:2f:17:1b:c9:dc:d3:30:42:
    c6:2a:91:c7:95:ef:ff:21:9d:04:ab:1e:cf:d7:92:
    0a:8b:2a:0a:fe
ASN1 OID: SM2
writing EC key
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIIpSI9XVl/ve+43EDqBw0Kul7W1mms3GXSYh416QISg+oAoGCCqBHM9V
AYItoUQDQgAEmxmSy4HOD2d2sakaJTw0QFhRGZs25umcKzmg12FAsYNjVRmtLxcb
ydzTMELGKpHHle//IZ0Eqx7P15IKiyoK/g==
-----END EC PRIVATE KEY-----

5、工具类
BCECUtil.java

package GMSM;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X962Parameters;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9ECPoint;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECKeyParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.bouncycastle.util.io.pem.PemWriter;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * 这个工具类的方法,也适用于其他基于BC库的ECC算法
 */
public class BCECUtil {
	
    private static final String ALGO_NAME_EC = "EC";
    private static final String PEM_STRING_PUBLIC = "PUBLIC KEY";
    private static final String PEM_STRING_ECPRIVATEKEY = "EC PRIVATE KEY";

    /**
     * 生成ECC密钥对
     *
     * @return ECC密钥对
     */
    public static AsymmetricCipherKeyPair generateKeyPairParameter(
            ECDomainParameters domainParameters, SecureRandom random) {
        ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParameters,
                random);
        ECKeyPairGenerator keyGen = new ECKeyPairGenerator();
        keyGen.init(keyGenerationParams);
        return keyGen.generateKeyPair();
    }

    public static KeyPair generateKeyPair(ECDomainParameters domainParameters, SecureRandom random)
            throws NoSuchProviderException, NoSuchAlgorithmException,
            InvalidAlgorithmParameterException {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME);
        ECParameterSpec parameterSpec = new ECParameterSpec(domainParameters.getCurve(), domainParameters.getG(),
                domainParameters.getN(), domainParameters.getH());
        kpg.initialize(parameterSpec, random);
        return kpg.generateKeyPair();
    }

    public static int getCurveLength(ECKeyParameters ecKey) {
        return getCurveLength(ecKey.getParameters());
    }

    public static int getCurveLength(ECDomainParameters domainParams) {
        return (domainParams.getCurve().getFieldSize() + 7) / 8;
    }

    public static byte[] fixToCurveLengthBytes(int curveLength, byte[] src) {
        if (src.length == curveLength) {
            return src;
        }

        byte[] result = new byte[curveLength];
        if (src.length > curveLength) {
            System.arraycopy(src, src.length - result.length, result, 0, result.length);
        } else {
            System.arraycopy(src, 0, result, result.length - src.length, src.length);
        }
        return result;
    }

    /**
     * @param dHex             十六进制字符串形式的私钥d值,如果是SM2算法,Hex字符串长度应该是64(即32字节)
     * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
     * @return
     */
    public static ECPrivateKeyParameters createECPrivateKeyParameters(
            String dHex, ECDomainParameters domainParameters) {
        return createECPrivateKeyParameters(ByteUtils.fromHexString(dHex), domainParameters);
    }

    /**
     * @param dBytes           字节数组形式的私钥d值,如果是SM2算法,应该是32字节
     * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
     * @return
     */
    public static ECPrivateKeyParameters createECPrivateKeyParameters(
            byte[] dBytes, ECDomainParameters domainParameters) {
        return createECPrivateKeyParameters(new BigInteger(1, dBytes), domainParameters);
    }

    /**
     * @param d                大数形式的私钥d值
     * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
     * @return
     */
    public static ECPrivateKeyParameters createECPrivateKeyParameters(
            BigInteger d, ECDomainParameters domainParameters) {
        return new ECPrivateKeyParameters(d, domainParameters);
    }

    /**
     * 根据EC私钥构造EC公钥
     *
     * @param priKey ECC私钥参数对象
     * @return
     */
    public static ECPublicKeyParameters buildECPublicKeyByPrivateKey(ECPrivateKeyParameters priKey) {
        ECDomainParameters domainParameters = priKey.getParameters();
        ECPoint q = new FixedPointCombMultiplier().multiply(domainParameters.getG(), priKey.getD());
        return new ECPublicKeyParameters(q, domainParameters);
    }

    /**
     * @param x                大数形式的公钥x分量
     * @param y                大数形式的公钥y分量
     * @param curve            EC曲线参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#CURVE}
     * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
     * @return
     */
    public static ECPublicKeyParameters createECPublicKeyParameters(
            BigInteger x, BigInteger y, ECCurve curve, ECDomainParameters domainParameters) {
        return createECPublicKeyParameters(x.toByteArray(), y.toByteArray(), curve, domainParameters);
    }

    /**
     * @param xHex             十六进制形式的公钥x分量,如果是SM2算法,Hex字符串长度应该是64(即32字节)
     * @param yHex             十六进制形式的公钥y分量,如果是SM2算法,Hex字符串长度应该是64(即32字节)
     * @param curve            EC曲线参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#CURVE}
     * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
     * @return
     */
    public static ECPublicKeyParameters createECPublicKeyParameters(
            String xHex, String yHex, ECCurve curve, ECDomainParameters domainParameters) {
        return createECPublicKeyParameters(ByteUtils.fromHexString(xHex), ByteUtils.fromHexString(yHex),
                curve, domainParameters);
    }

    /**
     * @param xBytes           十六进制形式的公钥x分量,如果是SM2算法,应该是32字节
     * @param yBytes           十六进制形式的公钥y分量,如果是SM2算法,应该是32字节
     * @param curve            EC曲线参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#CURVE}
     * @param domainParameters EC Domain参数,一般是固定的,如果是SM2算法的可参考{@link SM2Util#DOMAIN_PARAMS}
     * @return
     */
    public static ECPublicKeyParameters createECPublicKeyParameters(
            byte[] xBytes, byte[] yBytes, ECCurve curve, ECDomainParameters domainParameters) {
        final byte uncompressedFlag = 0x04;
        int curveLength = getCurveLength(domainParameters);
        xBytes = fixToCurveLengthBytes(curveLength, xBytes);
        yBytes = fixToCurveLengthBytes(curveLength, yBytes);
        byte[] encodedPubKey = new byte[1 + xBytes.length + yBytes.length];
        encodedPubKey[0] = uncompressedFlag;
        System.arraycopy(xBytes, 0, encodedPubKey, 1, xBytes.length);
        System.arraycopy(yBytes, 0, encodedPubKey, 1 + xBytes.length, yBytes.length);
        return new ECPublicKeyParameters(curve.decodePoint(encodedPubKey), domainParameters);
    }

    public static ECPrivateKeyParameters convertPrivateKeyToParameters(BCECPrivateKey ecPriKey) {
        ECParameterSpec parameterSpec = ecPriKey.getParameters();
        ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(),
                parameterSpec.getN(), parameterSpec.getH());
        return new ECPrivateKeyParameters(ecPriKey.getD(), domainParameters);
    }

    public static ECPublicKeyParameters convertPublicKeyToParameters(BCECPublicKey ecPubKey) {
        ECParameterSpec parameterSpec = ecPubKey.getParameters();
        ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(),
                parameterSpec.getN(), parameterSpec.getH());
        return new ECPublicKeyParameters(ecPubKey.getQ(), domainParameters);
    }

    public static BCECPublicKey createPublicKeyFromSubjectPublicKeyInfo(SubjectPublicKeyInfo subPubInfo)
            throws NoSuchProviderException,
            NoSuchAlgorithmException, InvalidKeySpecException, IOException {
        return BCECUtil.convertX509ToECPublicKey(subPubInfo.toASN1Primitive().getEncoded(ASN1Encoding.DER));
    }

    /**
     * 将ECC私钥转换为PKCS8标准的字节流
     *
     * @param priKey
     * @param pubKey 可以为空,但是如果为空的话得到的结果OpenSSL可能解析不了
     * @return
     */
    public static byte[] convertECPrivateKeyToPKCS8(
            ECPrivateKeyParameters priKey, ECPublicKeyParameters pubKey) {
        ECDomainParameters domainParams = priKey.getParameters();
        ECParameterSpec spec = new ECParameterSpec(domainParams.getCurve(), domainParams.getG(),
                domainParams.getN(), domainParams.getH());
        BCECPublicKey publicKey = null;
        if (pubKey != null) {
            publicKey = new BCECPublicKey(ALGO_NAME_EC, pubKey, spec,
                    BouncyCastleProvider.CONFIGURATION);
        }
        BCECPrivateKey privateKey = new BCECPrivateKey(ALGO_NAME_EC, priKey, publicKey,
                spec, BouncyCastleProvider.CONFIGURATION);
        return privateKey.getEncoded();
    }

    /**
     * 将PKCS8标准的私钥字节流转换为私钥对象
     *
     * @param pkcs8Key
     * @return
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws InvalidKeySpecException
     */
    public static BCECPrivateKey convertPKCS8ToECPrivateKey(byte[] pkcs8Key)
            throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
        PKCS8EncodedKeySpec peks = new PKCS8EncodedKeySpec(pkcs8Key);
        KeyFactory kf = KeyFactory.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME);
        return (BCECPrivateKey) kf.generatePrivate(peks);
    }

    /**
     * 将PKCS8标准的私钥字节流转换为PEM
     *
     * @param encodedKey
     * @return
     * @throws IOException
     */
    public static String convertECPrivateKeyPKCS8ToPEM(byte[] encodedKey) throws IOException {
        return convertEncodedDataToPEM(PEM_STRING_ECPRIVATEKEY, encodedKey);
    }

    /**
     * 将PEM格式的私钥转换为PKCS8标准字节流
     *
     * @param pemString
     * @return
     * @throws IOException
     */
    public static byte[] convertECPrivateKeyPEMToPKCS8(String pemString) throws IOException {
        return convertPEMToEncodedData(pemString);
    }

    /**
     * 将ECC私钥转换为SEC1标准的字节流
     * openssl d2i_ECPrivateKey函数要求的DER编码的私钥也是SEC1标准的,
     * 这个工具函数的主要目的就是为了能生成一个openssl可以直接“识别”的ECC私钥.
     * 相对RSA私钥的PKCS1标准,ECC私钥的标准为SEC1
     *
     * @param priKey
     * @param pubKey
     * @return
     * @throws IOException
     */
    public static byte[] convertECPrivateKeyToSEC1(
            ECPrivateKeyParameters priKey, ECPublicKeyParameters pubKey) throws IOException {
        byte[] pkcs8Bytes = convertECPrivateKeyToPKCS8(priKey, pubKey);
        PrivateKeyInfo pki = PrivateKeyInfo.getInstance(pkcs8Bytes);
        ASN1Encodable encodable = pki.parsePrivateKey();
        ASN1Primitive primitive = encodable.toASN1Primitive();
        byte[] sec1Bytes = primitive.getEncoded();
        return sec1Bytes;
    }

    /**
     * 将SEC1标准的私钥字节流恢复为PKCS8标准的字节流
     *
     * @param sec1Key
     * @return
     * @throws IOException
     */
    public static byte[] convertECPrivateKeySEC1ToPKCS8(byte[] sec1Key) throws IOException {
        /**
         * 参考org.bouncycastle.asn1.pkcs.PrivateKeyInfo和
         * org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey,逆向拼装
         */
        X962Parameters params = getDomainParametersFromName(SM2Util.JDK_EC_SPEC, false);
        ASN1OctetString privKey = new DEROctetString(sec1Key);
        ASN1EncodableVector v = new ASN1EncodableVector();
        v.add(new ASN1Integer(0)); //版本号
        v.add(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params)); //算法标识
        v.add(privKey);
        DERSequence ds = new DERSequence(v);
        return ds.getEncoded(ASN1Encoding.DER);
    }

    /**
     * 将SEC1标准的私钥字节流转为BCECPrivateKey对象
     *
     * @param sec1Key
     * @return
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws InvalidKeySpecException
     * @throws IOException
     */
    public static BCECPrivateKey convertSEC1ToBCECPrivateKey(byte[] sec1Key)
            throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, IOException {
        PKCS8EncodedKeySpec peks = new PKCS8EncodedKeySpec(convertECPrivateKeySEC1ToPKCS8(sec1Key));
        KeyFactory kf = KeyFactory.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME);
        return (BCECPrivateKey) kf.generatePrivate(peks);
    }

    /**
     * 将SEC1标准的私钥字节流转为ECPrivateKeyParameters对象
     * openssl i2d_ECPrivateKey函数生成的DER编码的ecc私钥是:SEC1标准的、带有EC_GROUP、带有公钥的,
     * 这个工具函数的主要目的就是为了使Java程序能够“识别”openssl生成的ECC私钥
     *
     * @param sec1Key
     * @return
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws InvalidKeySpecException
     */
    public static ECPrivateKeyParameters convertSEC1ToECPrivateKey(byte[] sec1Key)
            throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, IOException {
        BCECPrivateKey privateKey = convertSEC1ToBCECPrivateKey(sec1Key);
        return convertPrivateKeyToParameters(privateKey);
    }

    /**
     * 将ECC公钥对象转换为X509标准的字节流
     *
     * @param pubKey
     * @return
     */
    public static byte[] convertECPublicKeyToX509(ECPublicKeyParameters pubKey) {
        ECDomainParameters domainParams = pubKey.getParameters();
        ECParameterSpec spec = new ECParameterSpec(domainParams.getCurve(), domainParams.getG(),
                domainParams.getN(), domainParams.getH());
        BCECPublicKey publicKey = new BCECPublicKey(ALGO_NAME_EC, pubKey, spec,
                BouncyCastleProvider.CONFIGURATION);
        return publicKey.getEncoded();
    }

    /**
     * 将X509标准的公钥字节流转为公钥对象
     *
     * @param x509Bytes
     * @return
     * @throws NoSuchProviderException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static BCECPublicKey convertX509ToECPublicKey(byte[] x509Bytes) throws NoSuchProviderException,
            NoSuchAlgorithmException, InvalidKeySpecException {
        X509EncodedKeySpec eks = new X509EncodedKeySpec(x509Bytes);
        KeyFactory kf = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
        return (BCECPublicKey) kf.generatePublic(eks);
    }

    /**
     * 将X509标准的公钥字节流转为PEM
     *
     * @param encodedKey
     * @return
     * @throws IOException
     */
    public static String convertECPublicKeyX509ToPEM(byte[] encodedKey) throws IOException {
        return convertEncodedDataToPEM(PEM_STRING_PUBLIC, encodedKey);
    }

    /**
     * 将PEM格式的公钥转为X509标准的字节流
     *
     * @param pemString
     * @return
     * @throws IOException
     */
    public static byte[] convertECPublicKeyPEMToX509(String pemString) throws IOException {
        return convertPEMToEncodedData(pemString);
    }

    /**
     * copy from BC
     *
     * @param genSpec
     * @return
     */
    public static X9ECParameters getDomainParametersFromGenSpec(ECGenParameterSpec genSpec) {
        return getDomainParametersFromName(genSpec.getName());
    }

    /**
     * copy from BC
     *
     * @param curveName
     * @return
     */
    public static X9ECParameters getDomainParametersFromName(String curveName) {
        X9ECParameters domainParameters;
        try {
            if (curveName.charAt(0) >= '0' && curveName.charAt(0) <= '2') {
                ASN1ObjectIdentifier oidID = new ASN1ObjectIdentifier(curveName);
                domainParameters = ECUtil.getNamedCurveByOid(oidID);
            } else {
                if (curveName.indexOf(' ') > 0) {
                    curveName = curveName.substring(curveName.indexOf(' ') + 1);
                    domainParameters = ECUtil.getNamedCurveByName(curveName);
                } else {
                    domainParameters = ECUtil.getNamedCurveByName(curveName);
                }
            }
        } catch (IllegalArgumentException ex) {
            domainParameters = ECUtil.getNamedCurveByName(curveName);
        }
        return domainParameters;
    }

    /**
     * copy from BC
     *
     * @param ecSpec
     * @param withCompression
     * @return
     */
    public static X962Parameters getDomainParametersFromName(
            java.security.spec.ECParameterSpec ecSpec, boolean withCompression) {
        X962Parameters params;

        if (ecSpec instanceof ECNamedCurveSpec) {
            ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec) ecSpec).getName());
            if (curveOid == null) {
                curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec) ecSpec).getName());
            }
            params = new X962Parameters(curveOid);
        } else if (ecSpec == null) {
            params = new X962Parameters(DERNull.INSTANCE);
        } else {
            ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());

            X9ECParameters ecP = new X9ECParameters(
                    curve,
                    new X9ECPoint(EC5Util.convertPoint(curve, ecSpec.getGenerator()), withCompression),
                    ecSpec.getOrder(),
                    BigInteger.valueOf(ecSpec.getCofactor()),
                    ecSpec.getCurve().getSeed());

             如果是1.62或更低版本的bcprov-jdk15on应该使用以下这段代码,因为高版本的EC5Util.convertPoint没有向下兼容
            /*
            X9ECParameters ecP = new X9ECParameters(
                curve,
                EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression),
                ecSpec.getOrder(),
                BigInteger.valueOf(ecSpec.getCofactor()),
                ecSpec.getCurve().getSeed());
            */

            params = new X962Parameters(ecP);
        }

        return params;
    }

    private static String convertEncodedDataToPEM(String type, byte[] encodedData) throws IOException {
        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
        PemWriter pWrt = new PemWriter(new OutputStreamWriter(bOut));
        try {
            PemObject pemObj = new PemObject(type, encodedData);
            pWrt.writeObject(pemObj);
        } finally {
            pWrt.close();
        }
        return new String(bOut.toByteArray());
    }

    private static byte[] convertPEMToEncodedData(String pemString) throws IOException {
        ByteArrayInputStream bIn = new ByteArrayInputStream(pemString.getBytes());
        PemReader pRdr = new PemReader(new InputStreamReader(bIn));
        try {
            PemObject pemObject = pRdr.readPemObject();
            return pemObject.getContent();
        } finally {
            pRdr.close();
        }
    }
}

SM2Util.java

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.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.custom.gm.SM2P256V1Curve;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
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.Security;
import java.security.spec.ECFieldFp;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.EllipticCurve;

/**
 * SM2 工具类
 */
public class SM2Util {
	
	static {
        Security.addProvider(new BouncyCastleProvider());
    }
	
    //
    /*
     * 以下为SM2推荐曲线参数
     */
    public static final SM2P256V1Curve CURVE = new SM2P256V1Curve();
    public final static BigInteger SM2_ECC_P = CURVE.getQ();
    public final static BigInteger SM2_ECC_A = CURVE.getA().toBigInteger();
    public final static BigInteger SM2_ECC_B = CURVE.getB().toBigInteger();
    public final static BigInteger SM2_ECC_N = CURVE.getOrder();
    public final static BigInteger SM2_ECC_H = CURVE.getCofactor();
    public final static BigInteger SM2_ECC_GX = new BigInteger(
            "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16);
    public final static BigInteger SM2_ECC_GY = new BigInteger(
            "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16);
    public static final ECPoint G_POINT = CURVE.createPoint(SM2_ECC_GX, SM2_ECC_GY);
    public static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, G_POINT,
            SM2_ECC_N, SM2_ECC_H);
    public static final int CURVE_LEN = BCECUtil.getCurveLength(DOMAIN_PARAMS);
    //

    public static final EllipticCurve JDK_CURVE = new EllipticCurve(new ECFieldFp(SM2_ECC_P), SM2_ECC_A, SM2_ECC_B);
    public static final java.security.spec.ECPoint JDK_G_POINT = new java.security.spec.ECPoint(
            G_POINT.getAffineXCoord().toBigInteger(), G_POINT.getAffineYCoord().toBigInteger());
    public static final java.security.spec.ECParameterSpec JDK_EC_SPEC = new java.security.spec.ECParameterSpec(
            JDK_CURVE, JDK_G_POINT, SM2_ECC_N, SM2_ECC_H.intValue());

    //

	
	// SM2推荐曲线名称
	public static final 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;
	}
	
    /**
     * 获取私钥(16进制字符串,头部不带00长度共64)
     * @param privateKey 私钥
     * @return
     */
    public static String getPriKeyHexString(PrivateKey privateKey){
        BCECPrivateKey s=(BCECPrivateKey)privateKey;
        String priKeyHexString = Hex.toHexString(s.getD().toByteArray());
        if(null!= priKeyHexString && priKeyHexString.length()==66 && "00".equals(priKeyHexString.substring(0,2))){
            return priKeyHexString.substring(2);
        }
        return priKeyHexString;
    }

    /**
     * 获取公钥(16进制字符串,头部带04长度共130)
     * @param publicKey
     * @return
     */
    public static String getPubKeyHexString(PublicKey publicKey){
        BCECPublicKey p=(BCECPublicKey)publicKey;
        return Hex.toHexString(p.getQ().getEncoded(false));
    }
    
}

6、写一个main方法

public static void main(String[] args) throws Exception {
		
		//openssl生成sm2公私钥对
		//公钥
		String pu1 = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEmxmSy4HOD2d2sakaJTw0QFhRGZs25umcKzmg12FAsYNjVRmtLxcbydzTMELGKpHHle//IZ0Eqx7P15IKiyoK/g==";
		//私钥
		String pr1 = "MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgilIj1dWX+977jcQOoHDQq6XtbWaazcZdJiHjXpAhKD6hRANCAASbGZLLgc4PZ3axqRolPDRAWFEZmzbm6ZwrOaDXYUCxg2NVGa0vFxvJ3NMwQsYqkceV7/8hnQSrHs/XkgqLKgr+";
		
		//公钥文件是x509格式,转换成ECPublicKey,提取真正的公钥
		byte[] x509pu1 = Base64.decode(pu1);
		BCECPublicKey bcecPublicKey = BCECUtil.convertX509ToECPublicKey(x509pu1);
		String s1 = ByteUtils.toHexString(bcecPublicKey.getQ().getEncoded(false));
		System.out.println("公钥:" + s1);
		
		//私钥文件是pkcs8格式,转换成ECPrivateKey,提取真正的私钥
		byte[] x509pr1 = Base64.decode(pr1);
		BCECPrivateKey bcecPrivateKey = BCECUtil.convertPKCS8ToECPrivateKey(x509pr1);
		String s2 = ByteUtils.toHexString(bcecPrivateKey.getD().toByteArray());
		//java会在私钥前加00,要去除
		s2 = s2.substring(2);
		System.out.println("私钥:" + s2);
		
		System.out.println("==========测试加密解密==========");
		
		//String publicKey = "049b1992cb81ce0f6776b1a91a253c34405851199b36e6e99c2b39a0d76140b183635519ad2f171bc9dcd33042c62a91c795efff219d04ab1ecfd7920a8b2a0afe";
		String publicKey = s1;
		String str = "11111111111111111111111111111111111111111111111111111111111";
		String en = encryptBySM2(str, Base64.toBase64String(Hex.decode(publicKey)) );
		System.out.println("加密后:" + en);
		//String privateKey = "8a5223d5d597fbdefb8dc40ea070d0aba5ed6d669acdc65d2621e35e9021283e";
		String privateKey = s2;
		String de = decryptBySM2(en, Base64.toBase64String(Hex.decode(privateKey)) );
		System.out.println("解密后:" + de);
		
		System.out.println("==========测试签名验签==========");
		
		String sign = sign(Base64.toBase64String(Hex.decode(privateKey)), str);
		System.out.println("sign:" + sign);
		boolean flag = verify(Base64.toBase64String(Hex.decode(publicKey)), str, sign);
		System.out.println("验签结果:" + flag);
	}

执行结果:

公钥:049b1992cb81ce0f6776b1a91a253c34405851199b36e6e99c2b39a0d76140b183635519ad2f171bc9dcd33042c62a91c795efff219d04ab1ecfd7920a8b2a0afe
私钥:8a5223d5d597fbdefb8dc40ea070d0aba5ed6d669acdc65d2621e35e9021283e
==========测试加密解密==========
加密后:BKIe16SRgAgHGgOalBDv1xtIONHVQtrfCXpnVnH2iOwhKoXXYLCDrp8FlcNtJwnpJxtF9soslWvO+HhfpmW4knRBGk+NLs9ftf+mSeNaVkFG2ySt0tIjUKHl51GUZlkW1yQ/DRm7w9BNheJgow+PX5jorKhzM1IIv2AshlNFQg9Yut066j+5dLtE+no5/32O9+4dP80z6jGc+FcI
解密后:11111111111111111111111111111111111111111111111111111111111
==========测试签名验签==========
sign:MEUCICKGZZ5X1JAFa40/vtqD4de+HKh9rAiWaAtiUKVAODPgAiEAkXcnA3OxcXk0latkmmr3KS5ptwBI6PiPVJcyJJrnSp8=
验签结果:true

密钥和命令解析出来的一样。
依赖包:bcprov-jdk15on-1.70.jar

SM2加密算法的结果长度,取决于明文长度,没记错的话是C1+C2+C3, C1和C3是定长,分别为64字节和32字节,C2的长度等于明文长度
准确的计算过程,可以看看GM/T 0003.4

用密钥明文16进制转16进制字符串再base64编码:

    public static void main(String[] args) throws Exception {
    	//公钥明文字符串转base64编码,公钥明文字符串128字节长度不带04
		String pu2 = "OWIxOTkyY2I4MWNlMGY2Nzc2YjFhOTFhMjUzYzM0NDA1ODUxMTk5YjM2ZTZlOTljMmIzOWEwZDc2MTQwYjE4MzYzNTUxOWFkMmYxNzFiYzlkY2QzMzA0MmM2MmE5MWM3OTVlZmZmMjE5ZDA0YWIxZWNmZDc5MjBhOGIyYTBhZmU=";
		//私钥明文字符串转base64编码,私钥明文字符串64字节长度
		String pr2 = "OGE1MjIzZDVkNTk3ZmJkZWZiOGRjNDBlYTA3MGQwYWJhNWVkNmQ2NjlhY2RjNjVkMjYyMWUzNWU5MDIxMjgzZQ==";
		
		//base64解码
		String s3 = "04" + new String(Base64.decode(pu2)); //公钥前加04
		String s4 = new String(Base64.decode(pr2));
		System.out.println("公钥:" + s3);
		System.out.println("私钥:" + s4);
		
		byte pub2[] = Hex.decode("04" + new String(Base64.decode(pu2))); 
		byte pri2[] = Hex.decode(new String(Base64.decode(pr2)));
		
		String str = "11111111111111111111111111111111111111111111111111111111111";
		
		System.out.println("==========测试加密解密==========");
		
		String en = encryptBySM2(str, Base64.toBase64String(pub2));
		System.out.println("加密后:" + en);
		String de = decryptBySM2(en, Base64.toBase64String(pri2));
		System.out.println("解密后:" + de);
		
		System.out.println("==========测试签名验签==========");
		
		String sign = sign(Base64.toBase64String(pri2), str);
		System.out.println("sign:" + sign);
		boolean flag = verify(Base64.toBase64String(pub2), str, sign);
		System.out.println("验签结果:" + flag);
		
	}

执行结果:

公钥:049b1992cb81ce0f6776b1a91a253c34405851199b36e6e99c2b39a0d76140b183635519ad2f171bc9dcd33042c62a91c795efff219d04ab1ecfd7920a8b2a0afe
私钥:8a5223d5d597fbdefb8dc40ea070d0aba5ed6d669acdc65d2621e35e9021283e
==========测试加密解密==========
加密后:BIPG2WfWmBz+xU/btAI796VF7RX53WYZfsujS2ICC5umdS7x6hIE+zDEJpStVY/rAYmJeqEsmNx2oHN/8rbWiDgkZJS2xZL50br+a2RThzmh2fcdFSy3MtNcvcFGB/FD/h+pm5ImdPO94HD9Efv3apNum3/V5sNxUOM08YMgXrIYx30osZzBE41NSfO7El2NM5Ryy7xj0qgPmq3j
解密后:11111111111111111111111111111111111111111111111111111111111
==========测试签名验签==========
sign:MEUCIQDTzmVPjkPqPvpV2hMJvqPIMNjakS5/iMY5aeIGIYjGAAIgOREKqHSC0bfb1RIbhGz80wKpU87qqFYYCxyjiI64CRg=
验签结果:true

用工具类生成密钥对:

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {

        String M="encryption standard";
        System.out.println("\n明文:"+ M);

        SM2Util sm2 = new SM2Util();
        KeyPair keyPair = sm2.geneSM2KeyPair();

        PublicKey publicKey = keyPair.getPublic();
        String pubKeyHexString = sm2.getPubKeyHexString(publicKey);
        System.out.println("公钥:"+ pubKeyHexString);

        PrivateKey privateKey = keyPair.getPrivate();
        String priKeyHexString = sm2.getPriKeyHexString(privateKey);
        System.out.println("私钥:"+ priKeyHexString);

        String cipherData = sm2.encrypt(pubKeyHexString, M);
        System.out.println("密文:" + cipherData);

        String text=sm2.decrypt(priKeyHexString, cipherData);
        System.out.println("解密:"+text);
    }

参考资料:
支付对接常用的加密方式介绍以及java代码实现_京东云官方的技术博客_51CTO博客

对于如何从SM2的pfx证书文件中解析公钥和私钥,并从二次加密的密文中解密 - 旧信 - 博客园 (cnblogs.com)

GitHub - ZZMarquis/gmhelper: 基于BC库:国密SM2/SM3/SM4算法简单封装;实现SM2 X509v3证书的签发;实现SM2 pfx证书的签发

有关sm2证书生成(openssl生成公私钥对)—使用的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  10. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

随机推荐