zoukankan      html  css  js  c++  java
  • RSA加解密,Java和C#互通

    一、使用场景

    Java作为服务端生成一对公私钥,C#作为客户端拥有公钥。

    RSA算法这里就不多做介绍了,可参考RSA算法介绍

    二、规范

    • 公私钥的形式都是base64字符串
    • 通过公私钥加密后的字符串也是base64字符串

    三、测试内容

    • C#用公钥加密,Java用私钥解密
    • Java用私钥加密,C#用公钥解密

    四、测试代码

    先给出测试代码,后面会有具体的源码。

    首先Java作为服务端生成一对公私钥

    // 生成一对公私钥
    KeyStore keyPair = RSAUtil.createKeys();
    System.out.println("公钥:" + keyPair.getPublicKey());
    System.out.println("私钥:" + keyPair.getPrivateKey());
    

    公钥

    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnEqBL90TN2naZicQyik333/BjG/f5Ib7P0n5j3O5IxqJwvISbim5Xg94u3UBmserNYPQWQt1Jb/HiAG5alrnwWB1XypRyyilVsARXblv1AtwG4Y5BWvDG0jsipmRj581TQSd1w2Nq6rw/xzUH2xzUx8MLmoJu1RiC1oCEUx1CgQIDAQAB

    私钥

    MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAKcSoEv3RM3adpmJxDKKTfff8GMb9/khvs/SfmPc7kjGonC8hJuKbleD3i7dQGax6s1g9BZC3Ulv8eIAblqWufBYHVfKlHLKKVWwBFduW/UC3AbhjkFa8MbSOyKmZGPnzVNBJ3XDY2rqvD/HNQfbHNTHwwuagm7VGILWgIRTHUKBAgMBAAECgYBqDOizT8Hc6U9fJpPjgOb88KkmQBqniUQVpweTXCnlCDEUXpXOJZDk/E+Mb+TxSv5AX8qQVuEgPG7NhniGCCb93qnKi5bU/rw+CQgm9er1pf97aIsvGobnw63bTE1HosWOfuHZTVwGybsQUGM3uPu8xxNaqRSlAknalniO9Y0AAQJBAPS0GZYPMAb+Jb4Yk1gD/joEFp3K5Uedk6dr+rDsNAV1gWBT3dOYjC0oUj5Ju3MQd8RxAm/CG6vDpRm4zRlbW6ECQQCuyRZ99uG+ViLqZlnCvOoTvb02VO/t+JnlW/ZexUIZZnuZPi1OxmnuAnA4prglkMI20KWLvXsxUFss6JGGx3rhAkEA3SWWqgL3yJoOygmzv6t8KjpfpiMjquFB++CeIx6UtQpI7iFdLEmFBZqXGSvReF+9nY70QMQP9d9OajyVPwFNgQJBAJ3nTymXi5PP75VByB/VWcdh5/mYvHWJ3UbHUQmHPdMCsfDcqkP7nqGGlGmpvL07TSoGTziygwQnK2ej6C0SNCECQQCOIO3vrXv4OJzU0dUdkmE5F3Yp/jIzsLpKG1wYE1cv6voRvrMLq8s2LL/5igQLV0XfM//PfQC2hdQIAoORnScc

    C#客户端,公钥加密

    加密内容:你好rsa

    RSA rsa = new RSA();
    string content = "你好rsa";
    string pubKey = @"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnEqBL90TN2naZicQyik333/BjG/f5Ib7P0n5j3O5IxqJwvISbim5Xg94u3UBmserNYPQWQt1Jb/HiAG5alrnwWB1XypRyyilVsARXblv1AtwG4Y5BWvDG0jsipmRj581TQSd1w2Nq6rw/xzUH2xzUx8MLmoJu1RiC1oCEUx1CgQIDAQAB";
    
    // 公钥加密
    string mi = rsa.EncryptByPublicKey(content, pubKey);
    

    得到加密后的字符串(base64格式):

    cLoDffoAxmu6AirAbwpentv7+LwrpD4UZ1OuGxRuhmao0mm6YhK8O3mkQAD235RJ/XlhK6dK8tp4XsBD4FFSCxcoyrnkdayWrGeWtRXRgYWPIpBrperinPXLt0rNYOvNVHWniewyAmPjrU1wfq7BqLZwYyXEGG9QFI83aZIT9xc=

    Java服务端,私钥解密

    // 公钥加密后的内容
    String mi = "cLoDffoAxmu6AirAbwpentv7+LwrpD4UZ1OuGxRuhmao0mm6YhK8O3mkQAD235RJ/XlhK6dK8tp4XsBD4FFSCxcoyrnkdayWrGeWtRXRgYWPIpBrperinPXLt0rNYOvNVHWniewyAmPjrU1wfq7BqLZwYyXEGG9QFI83aZIT9xc=";
    // 私钥
    String priKey = "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAKcSoEv3RM3adpmJxDKKTfff8GMb9/khvs/SfmPc7kjGonC8hJuKbleD3i7dQGax6s1g9BZC3Ulv8eIAblqWufBYHVfKlHLKKVWwBFduW/UC3AbhjkFa8MbSOyKmZGPnzVNBJ3XDY2rqvD/HNQfbHNTHwwuagm7VGILWgIRTHUKBAgMBAAECgYBqDOizT8Hc6U9fJpPjgOb88KkmQBqniUQVpweTXCnlCDEUXpXOJZDk/E+Mb+TxSv5AX8qQVuEgPG7NhniGCCb93qnKi5bU/rw+CQgm9er1pf97aIsvGobnw63bTE1HosWOfuHZTVwGybsQUGM3uPu8xxNaqRSlAknalniO9Y0AAQJBAPS0GZYPMAb+Jb4Yk1gD/joEFp3K5Uedk6dr+rDsNAV1gWBT3dOYjC0oUj5Ju3MQd8RxAm/CG6vDpRm4zRlbW6ECQQCuyRZ99uG+ViLqZlnCvOoTvb02VO/t+JnlW/ZexUIZZnuZPi1OxmnuAnA4prglkMI20KWLvXsxUFss6JGGx3rhAkEA3SWWqgL3yJoOygmzv6t8KjpfpiMjquFB++CeIx6UtQpI7iFdLEmFBZqXGSvReF+9nY70QMQP9d9OajyVPwFNgQJBAJ3nTymXi5PP75VByB/VWcdh5/mYvHWJ3UbHUQmHPdMCsfDcqkP7nqGGlGmpvL07TSoGTziygwQnK2ej6C0SNCECQQCOIO3vrXv4OJzU0dUdkmE5F3Yp/jIzsLpKG1wYE1cv6voRvrMLq8s2LL/5igQLV0XfM//PfQC2hdQIAoORnScc";
    // 解密后的内容
    String content = RSAUtil.decryptByPrivateKey(mi, priKey);
    
    System.out.println("你好rsa".equals(content)); // true
    

    至此,公钥加密,私钥解密测试通过。接下来测试Java私钥加密,C#公钥解密

    Java私钥加密

    加密内容:你好rsa

    // 私钥
    String priKey = "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAKcSoEv3RM3adpmJxDKKTfff8GMb9/khvs/SfmPc7kjGonC8hJuKbleD3i7dQGax6s1g9BZC3Ulv8eIAblqWufBYHVfKlHLKKVWwBFduW/UC3AbhjkFa8MbSOyKmZGPnzVNBJ3XDY2rqvD/HNQfbHNTHwwuagm7VGILWgIRTHUKBAgMBAAECgYBqDOizT8Hc6U9fJpPjgOb88KkmQBqniUQVpweTXCnlCDEUXpXOJZDk/E+Mb+TxSv5AX8qQVuEgPG7NhniGCCb93qnKi5bU/rw+CQgm9er1pf97aIsvGobnw63bTE1HosWOfuHZTVwGybsQUGM3uPu8xxNaqRSlAknalniO9Y0AAQJBAPS0GZYPMAb+Jb4Yk1gD/joEFp3K5Uedk6dr+rDsNAV1gWBT3dOYjC0oUj5Ju3MQd8RxAm/CG6vDpRm4zRlbW6ECQQCuyRZ99uG+ViLqZlnCvOoTvb02VO/t+JnlW/ZexUIZZnuZPi1OxmnuAnA4prglkMI20KWLvXsxUFss6JGGx3rhAkEA3SWWqgL3yJoOygmzv6t8KjpfpiMjquFB++CeIx6UtQpI7iFdLEmFBZqXGSvReF+9nY70QMQP9d9OajyVPwFNgQJBAJ3nTymXi5PP75VByB/VWcdh5/mYvHWJ3UbHUQmHPdMCsfDcqkP7nqGGlGmpvL07TSoGTziygwQnK2ej6C0SNCECQQCOIO3vrXv4OJzU0dUdkmE5F3Yp/jIzsLpKG1wYE1cv6voRvrMLq8s2LL/5igQLV0XfM//PfQC2hdQIAoORnScc";
    String content = "你好rsa";
    String mi = RSAUtil.encryptByPrivateKey(content, priKey);
    System.out.println(mi);
    

    加密后的内容:

    mm8zoUtHvX8nBnCeZQ6k0aPzbmsvgJiIlyG2QIu9bnsO9BikqUpPOU+4UD6pH60TqQuEJFC1Ynv4D2/K2XryfVLYlwNftuO3Ngem5eVwTkYYAeVnDZ7PlHvemuSV7KPNo/erMhVef1rh5avB/PtSP8tKzmMTJ0hQTVILIaRwhq8=

    C#公钥解密

    RSA rsa = new RSA();
    // Java私钥加密后的内容
    string mi = @"mm8zoUtHvX8nBnCeZQ6k0aPzbmsvgJiIlyG2QIu9bnsO9BikqUpPOU+4UD6pH60TqQuEJFC1Ynv4D2/K2XryfVLYlwNftuO3Ngem5eVwTkYYAeVnDZ7PlHvemuSV7KPNo/erMhVef1rh5avB/PtSP8tKzmMTJ0hQTVILIaRwhq8=";
    // 公钥
    string pubKey = @"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnEqBL90TN2naZicQyik333/BjG/f5Ib7P0n5j3O5IxqJwvISbim5Xg94u3UBmserNYPQWQt1Jb/HiAG5alrnwWB1XypRyyilVsARXblv1AtwG4Y5BWvDG0jsipmRj581TQSd1w2Nq6rw/xzUH2xzUx8MLmoJu1RiC1oCEUx1CgQIDAQAB";
    // 解密后的内容
    string content = rsa.DecryptByPublicKey(mi, pubKey);
    Assert.IsTrue("你好rsa" == content); // true
    

    五、源码

    Java源码

    需要apachecommons-codec.jar

    <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
        <version>1.11</version>
    </dependency>
    
    
    import org.apache.commons.codec.binary.Base64;
    
    import javax.crypto.Cipher;
    import java.security.KeyFactory;
    import java.security.KeyPair;
    import java.security.KeyPairGenerator;
    import java.security.interfaces.RSAPrivateKey;
    import java.security.interfaces.RSAPublicKey;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.security.spec.X509EncodedKeySpec;
    
    /**
     * RSA加解密工具<br>
     */
    public class RSAUtil {
        public static String RSA_ALGORITHM = "RSA";
        public static String UTF8 = "UTF-8";
    
        /**
         * 创建公钥私钥
         */
        public static KeyStore createKeys() throws Exception {
            KeyPairGenerator keyPairGeno = KeyPairGenerator.getInstance(RSA_ALGORITHM);
            keyPairGeno.initialize(1024);
            KeyPair keyPair = keyPairGeno.generateKeyPair();
    
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
    
            KeyStore keyStore = new KeyStore();
            keyStore.setPublicKey(Base64.encodeBase64String(publicKey.getEncoded()));
            keyStore.setPrivateKey(Base64.encodeBase64String(privateKey.getEncoded()));
            return keyStore;
        }
    
        /**
         * 获取公钥对象
         */
        public static RSAPublicKey getPublicKey(byte[] pubKeyData) throws Exception {
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKeyData);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        }
    
        /**
         * 获取公钥对象
         */
        public static RSAPublicKey getPublicKey(String pubKey) throws Exception {
            return getPublicKey(Base64.decodeBase64(pubKey));
    
        }
    
        /**
         * 获取私钥对象
         */
        public static RSAPrivateKey getPrivateKey(String priKey) throws Exception {
            return getPrivateKey(Base64.decodeBase64(priKey));
        }
    
        /**
         * 通过私钥byte[]将公钥还原,适用于RSA算法
         */
        public static RSAPrivateKey getPrivateKey(byte[] keyBytes) throws Exception {
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
    
        }
    
        public static String encryptByPublicKey(String data, String publicKey) throws Exception {
            return encryptByPublicKey(data, getPublicKey(publicKey));
        }
    
        /**
         * 公钥加密
         */
        public static String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] bytes = cipher.doFinal(data.getBytes(UTF8));
            return Base64.encodeBase64String(bytes);
        }
    
        public static String decryptByPublicKey(String data, String rsaPublicKey) throws Exception {
            return decryptByPublicKey(data, getPublicKey(rsaPublicKey));
        }
    
        /**
         * 公钥解密
         */
        public static String decryptByPublicKey(String data, RSAPublicKey rsaPublicKey) throws Exception {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey);
            byte[] inputData = Base64.decodeBase64(data);
            byte[] bytes = cipher.doFinal(inputData);
            return new String(bytes, UTF8);
        }
    
        public static String encryptByPrivateKey(String data, String privateKey) throws Exception {
            return encryptByPrivateKey(data, getPrivateKey(privateKey));
        }
    
        /**
         * 私钥加密
         */
        public static String encryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            byte[] bytes = cipher.doFinal(data.getBytes(UTF8));
            return Base64.encodeBase64String(bytes);
        }
    
        public static String decryptByPrivateKey(String data, String privateKey) throws Exception {
            return decryptByPrivateKey(data, getPrivateKey(privateKey));
        }
    
        /**
         * 私钥解密
         */
        public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] inputData = Base64.decodeBase64(data);
            byte[] bytes = cipher.doFinal(inputData);
            return new String(bytes, UTF8);
        }
    
        public static class KeyStore {
            private String publicKey;
            private String privateKey;
    
            public String getPublicKey() {
                return publicKey;
            }
    
            public void setPublicKey(String publicKey) {
                this.publicKey = publicKey;
            }
    
            public String getPrivateKey() {
                return privateKey;
            }
    
            public void setPrivateKey(String privateKey) {
                this.privateKey = privateKey;
            }
        }
    
    }
    

    C#源码

    需要下载一个组件

    下载地址:http://www.bouncycastle.org/csharp/download/bccrypto-csharp-1.8.2-bin.zip

    更多详情:http://www.bouncycastle.org/csharp/

    下载后添加dll即可

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Security.Cryptography;
    
    using Org.BouncyCastle.Asn1.Pkcs;
    using Org.BouncyCastle.Asn1.X509;
    using Org.BouncyCastle.Crypto.Generators;
    using Org.BouncyCastle.Crypto.Parameters;
    using Org.BouncyCastle.Math;
    using Org.BouncyCastle.Pkcs;
    using Org.BouncyCastle.Security;
    using Org.BouncyCastle.Crypto.Engines;
    using Org.BouncyCastle.X509;
    using Org.BouncyCastle.Crypto;
    using Org.BouncyCastle.Asn1;
    using Org.BouncyCastle.Crypto.Encodings;
    
    namespace Demo
    {
        public class RSA
        {
            private static Encoding Encoding_UTF8 = Encoding.UTF8;
    
            /// <summary>
            /// KEY 结构体
            /// </summary>
            public struct RSAKEY
            {
                /// <summary>
                /// 公钥
                /// </summary>
                public string PublicKey { get; set; }
                /// <summary>
                /// 私钥
                /// </summary>
                public string PrivateKey { get; set; }
            }
            public RSAKEY GetKey()
            {
                //RSA密钥对的构造器
                RsaKeyPairGenerator keyGenerator = new RsaKeyPairGenerator();
    
                //RSA密钥构造器的参数
                RsaKeyGenerationParameters param = new RsaKeyGenerationParameters(
                    Org.BouncyCastle.Math.BigInteger.ValueOf(3),
                    new Org.BouncyCastle.Security.SecureRandom(),
                    1024,   //密钥长度
                    25);
                //用参数初始化密钥构造器
                keyGenerator.Init(param);
                //产生密钥对
                AsymmetricCipherKeyPair keyPair = keyGenerator.GenerateKeyPair();
                //获取公钥和密钥
                AsymmetricKeyParameter publicKey = keyPair.Public;
                AsymmetricKeyParameter privateKey = keyPair.Private;
    
                SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey);
                PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKey);
    
                Asn1Object asn1ObjectPublic = subjectPublicKeyInfo.ToAsn1Object();
    
                byte[] publicInfoByte = asn1ObjectPublic.GetEncoded("UTF-8");
                Asn1Object asn1ObjectPrivate = privateKeyInfo.ToAsn1Object();
                byte[] privateInfoByte = asn1ObjectPrivate.GetEncoded("UTF-8");
    
                RSAKEY item = new RSAKEY()
                {
                    PublicKey = Convert.ToBase64String(publicInfoByte),
                    PrivateKey = Convert.ToBase64String(privateInfoByte)
                };
                return item;
            }
            private AsymmetricKeyParameter GetPublicKeyParameter(string keyBase64)
            {
                keyBase64 = keyBase64.Replace("
    ", "").Replace("
    ", "").Replace(" ", "");
                byte[] publicInfoByte = Convert.FromBase64String(keyBase64);
                Asn1Object pubKeyObj = Asn1Object.FromByteArray(publicInfoByte);//这里也可以从流中读取,从本地导入
                AsymmetricKeyParameter pubKey = PublicKeyFactory.CreateKey(publicInfoByte);
                return pubKey;
            }
    
            private AsymmetricKeyParameter GetPrivateKeyParameter(string keyBase64)
            {
                keyBase64 = keyBase64.Replace("
    ", "").Replace("
    ", "").Replace(" ", "");
                byte[] privateInfoByte = Convert.FromBase64String(keyBase64);
                // Asn1Object priKeyObj = Asn1Object.FromByteArray(privateInfoByte);//这里也可以从流中读取,从本地导入
                // PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKey);
                AsymmetricKeyParameter priKey = PrivateKeyFactory.CreateKey(privateInfoByte);
                return priKey;
            }
    
            /// <summary>
            /// 私钥加密
            /// </summary>
            /// <param name="data">加密内容</param>
            /// <param name="privateKey">私钥(Base64后的)</param>
            /// <returns>返回Base64内容</returns>
            public string EncryptByPrivateKey(string data, string privateKey)
            {
                //非对称加密算法,加解密用
                IAsymmetricBlockCipher engine = new Pkcs1Encoding(new RsaEngine());
    
                //加密
                try
                {
                    engine.Init(true, GetPrivateKeyParameter(privateKey));
                    byte[] byteData = Encoding_UTF8.GetBytes(data);
                    var ResultData = engine.ProcessBlock(byteData, 0, byteData.Length);
                    return Convert.ToBase64String(ResultData);
                    //Console.WriteLine("密文(base64编码):" + Convert.ToBase64String(testData) + Environment.NewLine);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    
            /// <summary>
            /// 私钥解密
            /// </summary>
            /// <param name="data">待解密的内容</param>
            /// <param name="privateKey">私钥(Base64编码后的)</param>
            /// <returns>返回明文</returns>
            public string DecryptByPrivateKey(string data, string privateKey)
            {
                data = data.Replace("
    ", "").Replace("
    ", "").Replace(" ", "");
                //非对称加密算法,加解密用
                IAsymmetricBlockCipher engine = new Pkcs1Encoding(new RsaEngine());
    
                //解密
                try
                {
                    engine.Init(false, GetPrivateKeyParameter(privateKey));
                    byte[] byteData = Convert.FromBase64String(data);
                    var ResultData = engine.ProcessBlock(byteData, 0, byteData.Length);
                    return Encoding_UTF8.GetString(ResultData);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    
            /// <summary>
            /// 公钥加密
            /// </summary>
            /// <param name="data">加密内容</param>
            /// <param name="publicKey">公钥(Base64编码后的)</param>
            /// <returns>返回Base64内容</returns>
            public string EncryptByPublicKey(string data, string publicKey)
            {
                //非对称加密算法,加解密用
                IAsymmetricBlockCipher engine = new Pkcs1Encoding(new RsaEngine());
    
                //加密
                try
                {
                    engine.Init(true, GetPublicKeyParameter(publicKey));
                    byte[] byteData = Encoding_UTF8.GetBytes(data);
                    var ResultData = engine.ProcessBlock(byteData, 0, byteData.Length);
                    return Convert.ToBase64String(ResultData);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    
            /// <summary>
            /// 公钥解密
            /// </summary>
            /// <param name="data">待解密的内容</param>
            /// <param name="publicKey">公钥(Base64编码后的)</param>
            /// <returns>返回明文</returns>
            public string DecryptByPublicKey(string data, string publicKey)
            {
                data = data.Replace("
    ", "").Replace("
    ", "").Replace(" ", "");
                //非对称加密算法,加解密用
                IAsymmetricBlockCipher engine = new Pkcs1Encoding(new RsaEngine());
    
                //解密
                try
                {
                    engine.Init(false, GetPublicKeyParameter(publicKey));
                    byte[] byteData = Convert.FromBase64String(data);
                    var ResultData = engine.ProcessBlock(byteData, 0, byteData.Length);
                    return Encoding_UTF8.GetString(ResultData);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }
    }
    
    

    此外还可以C#做服务端生成公私钥,Java做客户端。原理是一样的,这里就不测试了,读者可以自行实现。

  • 相关阅读:
    mouse_event模拟鼠标滚轮
    润乾报表配置技术路线
    建筑 物件 开心背单词 读句子,单词,字母,看图例, 翻译,看动画
    文字过渡动画,曲线过渡动画,,使用这个插件assign shape keys
    运动锻炼 开心背单词 读句子,单词,字母,看图例, 翻译,看动画,学英语,轻松背单词,简单背单词
    blender293 内置插件 精度绘画控件,PDT学习003,pdt tangents 切线
    日常用品 背单词 读句子 看图片 读单词 读字母 翻译, 看动画 学英语
    blender293 内置插件 精度绘画控件,PDT学习 precision drawing tools
    乔布斯 背单词 02 读句子 单词 字母 翻译,看动画 学英语 名言 我菜顾我在,我菜故我在,blender加python
    狐狸 和 乌鸦 英语 朗读句子 背单词
  • 原文地址:https://www.cnblogs.com/zhaoshujie/p/14666795.html
Copyright © 2011-2022 走看看