java pem rsa_RSA-java/RSA_PEM.java at master · wjch/RSA-java · GitHub

package com.github.xiangyuecn.rsajava;

import java.io.ByteArrayOutputStream;

import java.math.BigInteger;

import java.security.KeyFactory;

import java.security.interfaces.RSAPrivateKey;

import java.security.interfaces.RSAPublicKey;

import java.security.spec.RSAPrivateKeySpec;

import java.security.spec.RSAPublicKeySpec;

import java.util.Base64;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

/**

* RSA PEM格式秘钥对的解析和导出

*

* GitHub:https://github.com/xiangyuecn/RSA-java

*

* https://github.com/xiangyuecn/RSA-java/blob/master/RSA_PEM.java

* 移植自:https://github.com/xiangyuecn/RSA-csharp/blob/master/RSA_PEM.cs

*/

public class RSA_PEM {

/**modulus 模数,公钥、私钥都有**/

public byte[] Key_Modulus;

/**publicExponent 公钥指数,公钥、私钥都有**/

public byte[] Key_Exponent;

/**privateExponent 私钥指数,只有私钥的时候才有**/

public byte[] Key_D;

//以下参数只有私钥才有 https://docs.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.rsaparameters?redirectedfrom=MSDN&view=netframework-4.8

/**prime1**/

public byte[] Val_P;

/**prime2**/

public byte[] Val_Q;

/**exponent1**/

public byte[] Val_DP;

/**exponent2**/

public byte[] Val_DQ;

/**coefficient**/

public byte[] Val_InverseQ;

private RSA_PEM() {}

/***

* 通过公钥和私钥构造一个PEM

* @param publicKey 必须提供公钥

* @param privateKeyOrNull 私钥可以不提供,导出的PEM就只包含公钥

**/

public RSA_PEM(RSAPublicKey publicKey, RSAPrivateKey privateKeyOrNull) {

this(

BigB(publicKey.getModulus())

, BigB(publicKey.getPublicExponent())

, privateKeyOrNull==null?null:BigB(privateKeyOrNull.getPrivateExponent())

);

}

/***

* 通过全量的PEM字段数据构造一个PEM,除了模数modulus和公钥指数exponent必须提供外,其他私钥指数信息要么全部提供,要么全部不提供(导出的PEM就只包含公钥)

* 注意:所有参数首字节如果是0,必须先去掉

*/

public RSA_PEM(byte[] modulus, byte[] exponent, byte[] d, byte[] p, byte[] q, byte[] dp, byte[] dq, byte[] inverseQ) {

Key_Modulus=modulus;

Key_Exponent=exponent;

Key_D=d;

Val_P=p;

Val_Q=q;

Val_DP=dp;

Val_DQ=dq;

Val_InverseQ=inverseQ;

}

/***

* 通过公钥指数和私钥指数构造一个PEM,会反推计算出P、Q但和原始生成密钥的P、Q极小可能相同

* 注意:所有参数首字节如果是0,必须先去掉

* @param modulus 必须提供模数

* @param exponent 必须提供公钥指数

* @param dOrNull 私钥指数可以不提供,导出的PEM就只包含公钥

**/

public RSA_PEM(byte[] modulus, byte[] exponent, byte[] dOrNull) {

Key_Modulus=modulus;//modulus

Key_Exponent=exponent;//publicExponent

if(dOrNull!=null) {

Key_D=dOrNull;//privateExponent

//反推P、Q

BigInteger n = BigX(modulus);

BigInteger e = BigX(exponent);

BigInteger d = BigX(dOrNull);

BigInteger p = findFactor(e, d, n);

BigInteger q = n.divide(p);

if (p.compareTo(q) > 0) {

BigInteger t = p;

p = q;

q = t;

}

BigInteger exp1 = d.mod(p.subtract(BigInteger.ONE));

BigInteger exp2 = d.mod(q.subtract(BigInteger.ONE));

BigInteger coeff = q.modInverse(p);

Val_P=BigB(p);//prime1

Val_Q=BigB(q);//prime2

Val_DP=BigB(exp1);//exponent1

Val_DQ=BigB(exp2);//exponent2

Val_InverseQ=BigB(coeff);//coefficient

}

}

/**秘钥位数**/

public int keySize(){

return Key_Modulus.length*8;

}

/**是否包含私钥**/

public boolean hasPrivate(){

return Key_D!=null;

}

/**得到公钥Java对象**/

public RSAPublicKey getRSAPublicKey() throws Exception {

RSAPublicKeySpec spec=new RSAPublicKeySpec(BigX(Key_Modulus), BigX(Key_Exponent));

KeyFactory factory=KeyFactory.getInstance("RSA");

return (RSAPublicKey)factory.generatePublic(spec);

}

/**得到私钥Java对象**/

public RSAPrivateKey getRSAPrivateKey() throws Exception {

if(Key_D==null) {

throw new Exception("当前为公钥,无法获得私钥");

}

RSAPrivateKeySpec spec=new RSAPrivateKeySpec(BigX(Key_Modulus), BigX(Key_D));

KeyFactory factory=KeyFactory.getInstance("RSA");

return (RSAPrivateKey)factory.generatePrivate(spec);

}

/**转成正整数,如果是负数,需要加前导0转成正整数**/

static public BigInteger BigX(byte[] bigb) {

if(bigb[0]<0) {

byte[] c=new byte[bigb.length+1];

System.arraycopy(bigb,0,c,1,bigb.length);

bigb=c;

}

return new BigInteger(bigb);

}

/**BigInt导出byte整数首字节>0x7F的会加0前导,保证正整数,因此需要去掉0**/

static public byte[] BigB(BigInteger bigx) {

byte[] val=bigx.toByteArray();

if(val[0]==0) {

byte[] c=new byte[val.length-1];

System.arraycopy(val,1,c,0,c.length);

val=c;

}

return val;

}

/**

* 由n e d 反推 P Q

* 资料: https://stackoverflow.com/questions/43136036/how-to-get-a-rsaprivatecrtkey-from-a-rsaprivatekey

* https://v2ex.com/t/661736

***/

private static BigInteger findFactor(BigInteger e, BigInteger d, BigInteger n) {

BigInteger edMinus1 = e.multiply(d).subtract(BigInteger.ONE);

int s = edMinus1.getLowestSetBit();

BigInteger t = edMinus1.shiftRight(s);

long now=System.currentTimeMillis();

for (int aInt = 2; true; aInt++) {

if(aInt%10==0 && System.currentTimeMillis()-now>3000) {

throw new RuntimeException("推算RSA.P超时");//测试最多循环2次,1024位的速度很快 8ms

}

BigInteger aPow = BigInteger.valueOf(aInt).modPow(t, n);

for (int i = 1; i <= s; i++) {

if (aPow.equals(BigInteger.ONE)) {

break;

}

if (aPow.equals(n.subtract(BigInteger.ONE))) {

break;

}

BigInteger aPowSquared = aPow.multiply(aPow).mod(n);

if (aPowSquared.equals(BigInteger.ONE)) {

return aPow.subtract(BigInteger.ONE).gcd(n);

}

aPow = aPowSquared;

}

}

}

/**

* 用PEM格式密钥对创建RSA,支持PKCS#1、PKCS#8格式的PEM

*/

static public RSA_PEM FromPEM(String pem) throws Exception {

RSA_PEM param=new RSA_PEM();

String base64 = _PEMCode.matcher(pem).replaceAll("");

byte[] dataX = Base64.getDecoder().decode(base64);//java byte是正负数

if (dataX == null) {

throw new Exception("PEM内容无效");

}

short[] data=new short[dataX.length];//转成正整数的bytes数组,不然byte是负数难搞

for(int i=0;i

data[i]=(short)(dataX[i]&0xff);

}

int[] idx = new int[] {0};

if (pem.contains("PUBLIC KEY")) {

/****使用公钥****/

//读取数据总长度

readLen(0x30, data, idx);

//看看有没有oid

int[] idx2 = new int[] {idx[0]};

if (eq(_SeqOID, data, idx)) {

//读取1长度

readLen(0x03, data, idx);

idx[0]++;//跳过0x00

//读取2长度

readLen(0x30, data, idx);

}else {

idx = idx2;

}

//Modulus

param.Key_Modulus = readBlock(data, idx);

//Exponent

param.Key_Exponent = readBlock(data, idx);

} else if (pem.contains("PRIVATE KEY")) {

/****使用私钥****/

//读取数据总长度

readLen(0x30, data, idx);

//读取版本号

if (!eq(_Ver, data, idx)) {

throw new Exception("PEM未知版本");

}

//检测PKCS8

int[] idx2 = new int[] {idx[0]};

if (eq(_SeqOID, data, idx)) {

//读取1长度

readLen(0x04, data, idx);

//读取2长度

readLen(0x30, data, idx);

//读取版本号

if (!eq(_Ver, data, idx)) {

throw new Exception("PEM版本无效");

}

} else {

idx = idx2;

}

//读取数据

param.Key_Modulus = readBlock(data, idx);

param.Key_Exponent = readBlock(data, idx);

param.Key_D = readBlock(data, idx);

param.Val_P = readBlock(data, idx);

param.Val_Q = readBlock(data, idx);

param.Val_DP = readBlock(data, idx);

param.Val_DQ = readBlock(data, idx);

param.Val_InverseQ = readBlock(data, idx);

} else {

throw new Exception("pem需要BEGIN END标头");

}

return param;

}

static private Pattern _PEMCode = Pattern.compile("--+.+?--+|[\\s\\r\\n]+");

static private byte[] _SeqOID = new byte[] { 0x30, 0x0D, 0x06, 0x09, 0x2A, (byte)0x86, 0x48, (byte)0x86, (byte)0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };

static private byte[] _Ver = new byte[] { 0x02, 0x01, 0x00 };

/**从数组start开始到指定长度复制一份**/

static private byte[] sub(short[] arr, int start, int count) {

byte[] val = new byte[count];

for (int i = 0; i < count; i++) {

val[i] = (byte)arr[start + i];

}

return val;

}

/**读取长度**/

static private int readLen(int first, short[] data, int[] idxO) throws Exception {

int idx=idxO[0];

try {

if (data[idx] == first) {

idx++;

if (data[idx] == 0x81) {

idx++;

return data[idx++];

} else if (data[idx] == 0x82) {

idx++;

return (((int)data[idx++]) << 8) + data[idx++];

} else if (data[idx] < 0x80) {

return data[idx++];

}

}

throw new Exception("PEM未能提取到数据");

}finally {

idxO[0]=idx;

}

}

/**读取块数据**/

static private byte[] readBlock(short[] data, int[] idxO) throws Exception {

int idx=idxO[0];

try {

int len = readLen(0x02, data, idxO);

idx=idxO[0];

if (data[idx] == 0x00) {

idx++;

len--;

}

byte[] val = sub(data, idx, len);

idx += len;

return val;

}finally {

idxO[0]=idx;

}

}

/**比较data从idx位置开始是否是byts内容**/

static private boolean eq(byte[] byts, short[] data, int[] idxO) {

int idx=idxO[0];

try {

for (int i = 0; i < byts.length; i++, idx++) {

if (idx >= data.length) {

return false;

}

if ((byts[i]&0xff) != data[idx]) {

return false;

}

}

return true;

}finally {

idxO[0]=idx;

}

}

/***

* 将RSA中的密钥对转换成PEM格式

* ,usePKCS8=false时返回PKCS#1格式,否则返回PKCS#8格式

* ,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响

*/

public String ToPEM(boolean convertToPublic, boolean usePKCS8) throws Exception {

//https://www.jianshu.com/p/25803dd9527d

//https://www.cnblogs.com/ylz8401/p/8443819.html

//https://blog.csdn.net/jiayanhui2877/article/details/47187077

//https://blog.csdn.net/xuanshao_/article/details/51679824

//https://blog.csdn.net/xuanshao_/article/details/51672547

ByteArrayOutputStream ms = new ByteArrayOutputStream();

if (this.Key_D==null || convertToPublic) {

/****生成公钥****/

//写入总字节数,不含本段长度,额外需要24字节的头,后续计算好填入

ms.write(0x30);

int index1 = ms.size();

//固定内容

// encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"

ms.write(_SeqOID);

//从0x00开始的后续长度

ms.write(0x03);

int index2 = ms.size();

ms.write(0x00);

//后续内容长度

ms.write(0x30);

int index3 = ms.size();

//写入Modulus

writeBlock(Key_Modulus, ms);

//写入Exponent

writeBlock(Key_Exponent, ms);

//计算空缺的长度

byte[] byts = ms.toByteArray();

byts = writeLen(index3, byts, ms);

byts = writeLen(index2, byts, ms);

byts = writeLen(index1, byts, ms);

return "-----BEGIN PUBLIC KEY-----\n" + TextBreak(Base64.getEncoder().encodeToString(byts), 64) + "\n-----END PUBLIC KEY-----";

} else {

/****生成私钥****/

//写入总字节数,后续写入

ms.write(0x30);

int index1 = ms.size();

//写入版本号

ms.write(_Ver);

//PKCS8 多一段数据

int index2 = -1, index3 = -1;

if (usePKCS8) {

//固定内容

ms.write(_SeqOID);

//后续内容长度

ms.write(0x04);

index2 = ms.size();

//后续内容长度

ms.write(0x30);

index3 = ms.size();

//写入版本号

ms.write(_Ver);

}

//写入数据

writeBlock(Key_Modulus, ms);

writeBlock(Key_Exponent, ms);

writeBlock(Key_D, ms);

writeBlock(Val_P, ms);

writeBlock(Val_Q, ms);

writeBlock(Val_DP, ms);

writeBlock(Val_DQ, ms);

writeBlock(Val_InverseQ, ms);

//计算空缺的长度

byte[] byts = ms.toByteArray();

if (index2 != -1) {

byts = writeLen(index3, byts, ms);

byts = writeLen(index2, byts, ms);

}

byts = writeLen(index1, byts, ms);

String flag = " PRIVATE KEY";

if (!usePKCS8) {

flag = " RSA" + flag;

}

return "-----BEGIN" + flag + "-----\n" + TextBreak(Base64.getEncoder().encodeToString(byts), 64) + "\n-----END" + flag + "-----";

}

}

/**写入一个长度字节码**/

static private void writeLenByte(int len, ByteArrayOutputStream ms) {

if (len < 0x80) {

ms.write((byte)len);

} else if (len <= 0xff) {

ms.write(0x81);

ms.write((byte)len);

} else {

ms.write(0x82);

ms.write((byte)(len >> 8 & 0xff));

ms.write((byte)(len & 0xff));

}

}

/**写入一块数据**/

static private void writeBlock(byte[] byts, ByteArrayOutputStream ms) throws Exception {

boolean addZero = ((byts[0] & 0xff) >> 4) >= 0x8;

ms.write(0x02);

int len = byts.length + (addZero ? 1 : 0);

writeLenByte(len, ms);

if (addZero) {

ms.write(0x00);

}

ms.write(byts);

}

/**根据后续内容长度写入长度数据**/

static private byte[] writeLen(int index, byte[] byts, ByteArrayOutputStream ms) {

int len = byts.length - index;

ms.reset();

ms.write(byts, 0, index);

writeLenByte(len, ms);

ms.write(byts, index, len);

return ms.toByteArray();

}

/**把字符串按每行多少个字断行**/

static private String TextBreak(String text, int line) {

int idx = 0;

int len = text.length();

StringBuilder str = new StringBuilder();

while (idx < len) {

if (idx > 0) {

str.append('\n');

}

if (idx + line >= len) {

str.append(text.substring(idx));

} else {

str.append(text.substring(idx, idx+line));

}

idx += line;

}

return str.toString();

}

/***

* 将XML格式密钥转成PEM,支持公钥xml、私钥xml

*/

static public RSA_PEM FromXML(String xml) throws Exception {

RSA_PEM rtv=new RSA_PEM();

Matcher xmlM=xmlExp.matcher(xml);

if(!xmlM.find()) {

throw new Exception("XML内容不符合要求");

}

Matcher tagM=xmlTagExp.matcher(xmlM.group(1));

Base64.Decoder dec=Base64.getDecoder();

while(tagM.find()) {

String tag=tagM.group(1);

String b64=tagM.group(2);

byte[] val=dec.decode(b64);

switch(tag) {

case "Modulus":rtv.Key_Modulus=val;break;

case "Exponent":rtv.Key_Exponent=val;break;

case "D":rtv.Key_D=val;break;

case "P":rtv.Val_P=val;break;

case "Q":rtv.Val_Q=val;break;

case "DP":rtv.Val_DP=val;break;

case "DQ":rtv.Val_DQ=val;break;

case "InverseQ":rtv.Val_InverseQ=val;break;

}

}

if(rtv.Key_Modulus==null || rtv.Key_Exponent==null) {

throw new Exception("XML公钥丢失");

}

if(rtv.Key_D!=null) {

if(rtv.Val_P==null || rtv.Val_Q==null || rtv.Val_DP==null || rtv.Val_DQ==null || rtv.Val_InverseQ==null) {

return new RSA_PEM(rtv.Key_Modulus, rtv.Key_Exponent, rtv.Key_D);

}

}

return rtv;

}

static private Pattern xmlExp=Pattern.compile("\\s*([<>\\/\\+=\\w\\s]+)\\s*");

static private Pattern xmlTagExp=Pattern.compile("\\s*([^

/***

* 将RSA中的密钥对转换成XML格式

* ,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响

*/

public String ToXML(boolean convertToPublic) {

Base64.Encoder enc=Base64.getEncoder();

StringBuilder str=new StringBuilder();

str.append("");

str.append(""+enc.encodeToString(Key_Modulus)+"");

str.append(""+enc.encodeToString(Key_Exponent)+"");

if (this.Key_D==null || convertToPublic) {

/****生成公钥****/

//NOOP

} else {

/****生成私钥****/

str.append("

"+enc.encodeToString(Val_P)+"

");

str.append(""+enc.encodeToString(Val_Q)+"");

str.append(""+enc.encodeToString(Val_DP)+"");

str.append(""+enc.encodeToString(Val_DQ)+"");

str.append(""+enc.encodeToString(Val_InverseQ)+"");

str.append(""+enc.encodeToString(Key_D)+"");

}

str.append("");

return str.toString();

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值