RSA 数据加密及签名

原理:

   将两个大素数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
RSA 正是这个原因, 生成的密钥对其实也是可以互相替换的,A 作为公钥, B 就可以作为私钥解密, B作为公钥,A 就可以作为私钥解密。

但是生成的AB 长度差异较大。通常用短的作为公钥,以减少加密的算法成本,相对而言解密的成本变高。

私钥如果太小,敌人可以在 logn 的时间内破解 毕竟穷举就是 2^n

使用方式, 公钥就是在外部传播的密钥串, 私钥是所有人持有的。

加密传输时,发送人用公钥加密数据,发送给所有人,所有人通过私钥解密获得数据。
签名认证时,所有人用私钥加密数据,广播告知,其他人用公钥尝试解密校验,校验一致则密文未被篡改且是对应所有人发布的。实际操作时,通常对摘要信息进行处理完成对应的工作。 比如对发布的信息取 md5(SHA1 或SHA 256), 然后用私钥对相应的 md5 加密(生成签名)。 其他人接受到后,先生成摘要,然后与解密后的字串对比 来判断是否被篡改和一致性。
  1. 事实上Android apk 的签名也是与此一致。
Android 的签名信息主要保存在。 MANIFEST.MF、CERT.SF和CERT.RSA 中
流程是遍历除了签名文件意外的entry, 生成摘要(SHA1)。保存在 MANIFEST.MF
对MANIFEST.MF 进行rsa 私钥签名 防止篡改 并记录在CERT.SF中
CERT.RSA文件中保存了公钥、所采用的加密算法等信息
因此框架中读取公钥的算法也是通过这个文件来获取。
读取代码如下,
含义即 私钥 信息协议是PSCS7  公钥信息协议是 X509 签名算法是SHA1
   Signature signature, X509Certificate publicKey, OutputStream out)
              throws IOException, GeneralSecurityException {
                  SignerInfo signerInfo = new SignerInfo(
                  new X500Name(publicKey.getIssuerX500Principal().getName()),
                  publicKey.getSerialNumber(),
                  AlgorithmId.get("SHA1"),
                  AlgorithmId.get("RSA"),
                  signature.sign());
 
              PKCS7 pkcs7 = new PKCS7(
             new AlgorithmId[] { AlgorithmId.get("SHA1") },
             new ContentInfo(ContentInfo.DATA_OID, null),
             new X509Certificate[] { publicKey },
             new SignerInfo[] { signerInfo });
  1. 编码加密解密都是针对字节流,完成之后要对字节流编码。通常的方式是。 Base64.encode(bytes, Base64.DEFAULT) 然后 new String(code , “UTF-8”);
    对应的解码时也要按照base 64 解码

客户端和服务端代码案例:

生成密钥对代码

  /**
     * 生成密钥对
     *
     * @return
     */
    public Map<String, byte[]> generateKeyBytes() {

        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator
                    .getInstance(KEY_ALGORITHM);
            keyPairGenerator.initialize(KEY_SIZE);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            Map<String, byte[]> keyMap = new HashMap<String, byte[]>();
            keyMap.put(PUBLIC_KEY, publicKey.getEncoded());
            keyMap.put(PRIVATE_KEY, privateKey.getEncoded());
            return keyMap;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

服务端签名

/**
 * 还原私钥
 *
 * @param keyBytes
 * @return
 */
public PrivateKey restorePrivateKey(byte[] keyBytes) {
    PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
            keyBytes);
    try {
        KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateKey = factory
                .generatePrivate(pkcs8EncodedKeySpec);
        return privateKey;
    } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
        e.printStackTrace();
    }
    return null;
}

/**
 * 签名
 *
 * @param privateKey 私钥
 * @param plain_text 明文
 * @return
 */
public byte[] sign(PrivateKey privateKey, String plain_text) {
    MessageDigest messageDigest;
    byte[] signed = null;
    try {
        messageDigest = MessageDigest.getInstance(ENCODE_ALGORITHM);
        messageDigest.update(plain_text.getBytes());
        byte[] outputDigest_sign = messageDigest.digest();
        //System.out.println("SHA-256加密后-----》" +bytesToHexString(outputDigest_sign));
        Signature Sign = Signature.getInstance(SIGNATURE_ALGORITHM);
        Sign.initSign(privateKey);
        Sign.update(outputDigest_sign);
        signed = Sign.sign();
        //  System.out.println("SHA256withRSA签名后-----》" + bytesToHexString(signed));
    } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
        e.printStackTrace();
    }
    return signed;
}

public String base64Signed(PrivateKey privateKey, String plain_text) {
    return Base64.encodeBase64String(sign(privateKey, plain_text));
}

客户端验证

/**
 * Generates a PublicKey instance from a string containing the
 * Base64-encoded public key.
 *
 * @param encodedPublicKey Base64-encoded public key
 * @throws IllegalArgumentException if encodedPublicKey is invalid
 */
static PublicKey generatePublicKey(String encodedPublicKey) {
    try {
        byte[] decodedKey = Base64.decode(encodedPublicKey, Base64.DEFAULT);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
        return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException(e);
    } catch (InvalidKeySpecException e) {
        Log.e(TAG, "Invalid key specification.");
        throw new IllegalArgumentException(e);
    }
}

/**
 * Verifies that the signature from the server matches the computed
 * signature on the data.  Returns true if the data is correctly signed.
 *
 * @param publicKey  public key associated with the developer account
 * @param signedData signed data from server
 * @param signature  server signature
 * @return true if the data and signature match
 */
static boolean verify(PublicKey publicKey, String signedData, String signature) {
    MessageDigest messageDigest = null;
    try {
        messageDigest = MessageDigest.getInstance(ENCODE_ALGORITHM);
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        Log.e(TAG, "digest generate failed");
        return false;
    }
    messageDigest.update(signedData.getBytes());
    byte[] digestBytes = messageDigest.digest();


    try {
        Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
        sig.initVerify(publicKey);
        sig.update(digestBytes);
        byte[] decodedSignature = Base64.decode(signature, Base64.DEFAULT);
        if (!sig.verify(decodedSignature)) {
            Log.e(TAG, "Signature verification failed.");
            return false;
        }
        return true;
    } catch (NoSuchAlgorithmException e) {
        Log.e(TAG, "NoSuchAlgorithmException.");
    } catch (InvalidKeyException e) {
        Log.e(TAG, "Invalid key specification.");
    } catch (SignatureException e) {
        Log.e(TAG, "Signature exception.");
    }
    return false;
}

Android. 获取签名

    /**
     * 获取签名公钥
     *
     * @param mContext
     * @return
     */
    @Nullable
    public static String getSignInfo(Context mContext, String packageName) {
        if (TextUtils.isEmpty(packageName)) return "";
        String signCode = "";
        try {
            PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(
                    packageName, PackageManager.GET_SIGNATURES);
            Signature[] signs = packageInfo.signatures;
            Signature sign = signs[0];
//            String sha1 = signatureSHA1(signs);
            String sha256 = signatureSHA256(signs);
//            signCode = parseSignature(sign.toByteArray());
            signCode = sha256;
        } catch (Exception e) {
        }
        return signCode;
    }

    /**
     * SHA1
     */
    public static String signatureSHA1(Signature[] signatures) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-1");
            if (signatures != null) {
                for (Signature s : signatures)
                    digest.update(s.toByteArray());
            }
            return toHexString(digest.digest());
        } catch (Exception e) {
            return "";
        }
    }

    /**
     * 进行转换
     */
    public static String toHexString(byte[] bData) {
        StringBuilder sb = new StringBuilder(bData.length * 2);
        for (int i = 0; i < bData.length; i++) {
            sb.append(HEX_DIGITS[(bData[i] & 0xf0) >>> 4]);
            sb.append(HEX_DIGITS[bData[i] & 0x0f]);
        }
        return sb.toString();
    }

    /**
     * SHA256
     */
    public static String signatureSHA256(Signature[] signatures) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            if (signatures != null) {
                for (Signature s : signatures)
                    digest.update(s.toByteArray());
            }
            return toHexString(digest.digest());
        } catch (Exception e) {
            return "";
        }
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容