====== RSA Sample Code ======
[[https://javacardos.com/javacardforum/download/file.php?id=541| Download Sample Code ]] [[https://javacardos.com/javacardforum/viewtopic.php?f=31&t=737 |Discussion]]
/*
* @file RSASample.java
* @version v1.0
* Package AID: 4A617661436172644F53
* Applet AID: 4A617661436172644F5303
* @brief The ALgorithm of RSA Sample Code in JavaCard API Specification
* @comment The purpose of this example is only used to show the usage of API functions and there is no practical significance.
* @copyright Copyright(C) 2016 JavaCardOS Technologies Co., Ltd. All rights reserved.
*/
package JavaCardOS.Sample.Algorithm;
import javacard.framework.*;
import javacard.security.KeyBuilder;
import javacard.security.*;
import javacardx.crypto.*;
public class RSASample extends Applet
{
private static final byte INS_GEN_RSA_KEYPAIR = (byte)0x30;
private static final byte INS_GET_RSA_PUBKEY = (byte)0x31;
private static final byte INS_GET_RSA_PRIKEY = (byte)0x32;
private static final byte INS_SET_RSA_PUBKEY = (byte)0x33;
private static final byte INS_SET_RSA_PRIKEY = (byte)0x34;
private static final byte INS_RSA_SIGN = (byte)0x35;
private static final byte INS_RSA_VERIFY = (byte)0x36;
private static final byte INS_DO_RSA_CIPHER = (byte)0x37;
private byte[] tempBuffer;
private byte[] flags;
private static final short OFF_INS = (short)0;
private static final short OFF_P1 = (short)1;
private static final short OFF_P2 = (short)2;
private static final short OFF_LEN = (short)3;
private static final short FLAGS_SIZE = (short)5;
private static final byte ID_N = 0;
private static final byte ID_D = 1;
private static final byte ID_P = 2;
private static final byte ID_Q = 3;
private static final byte ID_PQ = 4;
private static final byte ID_DP1 = 5;
private static final byte ID_DQ1 = 6;
private byte[] rsaPubKey;
private short rsaPubKeyLen;
private byte[] rsaPriKey;
private short rsaPriKeyLen;
private boolean isRSAPriKeyCRT;
private Cipher rsaCipher;
private Signature rsaSignature;
private static final short SW_REFERENCE_DATA_NOT_FOUND = (short)0x6A88;
public RSASample()
{
//Create a transient byte array to store the temporary data
tempBuffer = JCSystem.makeTransientByteArray((short)256, JCSystem.CLEAR_ON_DESELECT);
flags = JCSystem.makeTransientByteArray(FLAGS_SIZE, JCSystem.CLEAR_ON_DESELECT);
rsaPubKey = new byte[(short) (256 + 32)];
rsaPriKey = new byte[(short)(128 * 5)];
rsaPubKeyLen = 0;
rsaPriKeyLen = 0;
isRSAPriKeyCRT = false;
rsaSignature = null;
//Create a RSA(not pad) object instance
rsaCipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false);
JCSystem.requestObjectDeletion();
}
public static void install(byte[] bArray, short bOffset, byte bLength)
{
new RSASample().register(bArray, (short) (bOffset + 1), bArray[bOffset]);
}
public void process(APDU apdu)
{
if (selectingApplet())
{
return;
}
byte[] buf = apdu.getBuffer();
short len = apdu.setIncomingAndReceive();
switch (buf[ISO7816.OFFSET_INS])
{
case INS_GEN_RSA_KEYPAIR:
//GEN_RSA_KEYPAIR
genRsaKeyPair(apdu, len);
break;
case INS_GET_RSA_PUBKEY:
//GET_RSA_PUBKEY
getRsaPubKey(apdu, len);
break;
case INS_GET_RSA_PRIKEY:
//GET_RSA_PRIKEY
getRsaPriKey(apdu, len);
break;
case INS_SET_RSA_PUBKEY:
// SET_RSA_PUBKEY
setRsaPubKey(apdu, len);
break;
case INS_SET_RSA_PRIKEY:
//SET_RSA_PRIKEY
setRsaPriKey(apdu, len);
break;
case INS_RSA_SIGN:
//RSA_SIGN
rsaSign(apdu, len);
break;
case INS_RSA_VERIFY:
//RSA_VERIFY
rsaVerify(apdu, len);
break;
case INS_DO_RSA_CIPHER:
//RSA_CIPHER
doRSACipher(apdu, len);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
}
//RSA algorithm encrypt and decrypt
private void doRSACipher(APDU apdu, short len)
{
byte[] buffer = apdu.getBuffer();
byte p1Tmp = buffer[ISO7816.OFFSET_P1];
boolean hasMoreCmd = (p1Tmp & 0x80) != 0;
boolean isEncrypt = (p1Tmp & 0x01) != 1;
short keyLen = (p1Tmp & 0x08) == (byte)0x00 ? KeyBuilder.LENGTH_RSA_1024 : KeyBuilder.LENGTH_RSA_2048;
short offset = (p1Tmp & 0x08) == (byte)0x00 ? (short)128 : (short)256;
if (len rsaPubKey.length)
{
Util.arrayFillNonAtomic(flags, (short)0, (short)flags.length, (byte)0);
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
}
//Copy the value of RSA public key to the global variable 'rsaPubKey'.
Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_CDATA, rsaPubKey, loadedLen, len);
loadedLen += len;
if ((buffer[ISO7816.OFFSET_P1] & 0x80) == 0) //last block
{
Util.arrayFillNonAtomic(flags, (short)0, (short)flags.length, (byte)0);
short modLen = (buffer[ISO7816.OFFSET_P1] & 0x01) == 0 ? (short)128 : (short)256;
if (loadedLen < modLen + 3 || loadedLen > modLen + 32)
{
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
}
rsaPubKeyLen = loadedLen;
}
else
{
Util.setShort(flags, OFF_LEN, loadedLen);
}
}
//Set the value of RSA private key
private void setRsaPriKey(APDU apdu, short len)
{
byte[] buffer = apdu.getBuffer();
if (buffer[ISO7816.OFFSET_P2] == 0) // first block
{
rsaPriKeyLen = (short)0;
Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_INS, flags, OFF_INS, (short)3);
Util.setShort(flags, OFF_LEN, (short)0);
}
else
{
if (flags[OFF_INS] != buffer[ISO7816.OFFSET_INS]
|| (flags[OFF_P1] & 0x7f) != (buffer[ISO7816.OFFSET_P1] & 0x7f)
|| (short)(flags[OFF_P2] & 0xff) != (short)((buffer[ISO7816.OFFSET_P2] & 0xff) - 1))
{
Util.arrayFillNonAtomic(flags, (short)0, (short)flags.length, (byte)0);
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
}
flags[OFF_P2] ++;
}
short loadedLen = Util.getShort(flags, OFF_LEN);
if (loadedLen + len > rsaPriKey.length)
{
Util.arrayFillNonAtomic(flags, (short)0, (short)flags.length, (byte)0);
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
}
//Copy the value of RSA private key to the global variable 'rsaPriKey'.
Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_CDATA, rsaPriKey, loadedLen, len);
loadedLen += len;
if ((buffer[ISO7816.OFFSET_P1] & 0x80) == 0) //last block
{
Util.arrayFillNonAtomic(flags, (short)0, (short)flags.length, (byte)0);
short modLen = (buffer[ISO7816.OFFSET_P1] & 0x01) == 0 ? (short)128 : (short)256;
boolean isCRT = (buffer[ISO7816.OFFSET_P1] & 0x40) != 0;
if ((isCRT && (loadedLen != modLen / 2 * 5)) || (!isCRT && (loadedLen != modLen * 2)))
{
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
}
isRSAPriKeyCRT = isCRT;
rsaPriKeyLen = loadedLen;
}
else
{
Util.setShort(flags, OFF_LEN, loadedLen);
}
}
//
private void genRsaKeyPair(APDU apdu, short len)
{
byte[] buffer = apdu.getBuffer();
short keyLen = buffer[ISO7816.OFFSET_P1] == 0 ? (short)1024 : (short)2048;
byte alg = buffer[ISO7816.OFFSET_P2] == 0 ? KeyPair.ALG_RSA : KeyPair.ALG_RSA_CRT;
KeyPair keyPair = new KeyPair(alg, keyLen);
if (len > 32)
{
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
if (len > 0)
{
((RSAPublicKey)keyPair.getPublic()).setExponent(buffer, ISO7816.OFFSET_CDATA, len);
}
//(Re)Initializes the key objects encapsulated in this KeyPair instance with new key values.
keyPair.genKeyPair();
JCSystem.beginTransaction();
rsaPubKeyLen = 0;
rsaPriKeyLen = 0;
JCSystem.commitTransaction();
//Get a reference to the public key component of this 'keyPair' object.
RSAPublicKey pubKey = (RSAPublicKey)keyPair.getPublic();
short pubKeyLen = 0;
//Store the RSA public key value in the global variable 'rsaPubKey', the public key contains modulo N and Exponent E
pubKeyLen += pubKey.getModulus(rsaPubKey, pubKeyLen);
pubKeyLen += pubKey.getExponent(rsaPubKey, pubKeyLen);
short priKeyLen = 0;
if (alg == KeyPair.ALG_RSA)
{
isRSAPriKeyCRT = false;
//Returns a reference to the private key component of this KeyPair object.
RSAPrivateKey priKey = (RSAPrivateKey)keyPair.getPrivate();
//RSA Algorithm, the Private Key contains N and D, and store these parameters value in global variable 'rsaPriKey'.
priKeyLen += priKey.getModulus(rsaPriKey, priKeyLen);
priKeyLen += priKey.getExponent(rsaPriKey, priKeyLen);
}
else //RSA CRT
{
isRSAPriKeyCRT = true;
//The RSAPrivateCrtKey interface is used to sign data using the RSA algorithm in its Chinese Remainder Theorem form.
RSAPrivateCrtKey priKey = (RSAPrivateCrtKey)keyPair.getPrivate();
//RSA CRT Algorithm, the Private Key contains P Q PQ DP and DQ, and store these parameters value in global variable 'rsaPriKey'.
priKeyLen += priKey.getP(rsaPriKey, priKeyLen);
priKeyLen += priKey.getQ(rsaPriKey, priKeyLen);
priKeyLen += priKey.getPQ(rsaPriKey, priKeyLen);
priKeyLen += priKey.getDP1(rsaPriKey, priKeyLen);
priKeyLen += priKey.getDQ1(rsaPriKey, priKeyLen);
}
JCSystem.beginTransaction();
rsaPubKeyLen = pubKeyLen;
rsaPriKeyLen = priKeyLen;
JCSystem.commitTransaction();
JCSystem.requestObjectDeletion();
}
//RSA Signature
private void rsaSign(APDU apdu, short len)
{
byte[] buffer = apdu.getBuffer();
if (rsaPriKeyLen == 0)
{
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
boolean hasMoreCmd = (buffer[ISO7816.OFFSET_P1] & 0x80) != 0;
short resultLen = 0;
if (buffer[ISO7816.OFFSET_P2] == 0) //first block
{
Key key;
if (!isRSAPriKeyCRT)
{
short ret;
//Creates uninitialized private keys for signature algorithms.
key = KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, (short)(rsaPriKeyLen / 2 * 8), false);
ret = getRsaPriKeyComponent(ID_N, tempBuffer, (short)0);
((RSAPrivateKey)key).setModulus(tempBuffer, (short)0, ret);
ret = getRsaPriKeyComponent(ID_D, tempBuffer, (short)0);
((RSAPrivateKey)key).setExponent(tempBuffer, (short)0, ret);
}
else
{
short ret;
//Creates uninitialized private keys for signature algorithms.
key = KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_CRT_PRIVATE, (short)(rsaPriKeyLen / 5 * 16), false);
ret = getRsaPriKeyComponent(ID_P, tempBuffer, (short)0);
((RSAPrivateCrtKey)key).setP(tempBuffer, (short)0, ret);
ret = getRsaPriKeyComponent(ID_Q, tempBuffer, (short)0);
((RSAPrivateCrtKey)key).setQ(tempBuffer, (short)0, ret);
ret = getRsaPriKeyComponent(ID_DP1, tempBuffer, (short)0);
((RSAPrivateCrtKey)key).setDP1(tempBuffer, (short)0, ret);
ret = getRsaPriKeyComponent(ID_DQ1, tempBuffer, (short)0);
((RSAPrivateCrtKey)key).setDQ1(tempBuffer, (short)0, ret);
ret = getRsaPriKeyComponent(ID_PQ, tempBuffer, (short)0);
((RSAPrivateCrtKey)key).setPQ(tempBuffer, (short)0, ret);
}
// Creates a Signature object instance of the ALG_RSA_SHA_PKCS1 algorithm.
rsaSignature = Signature.getInstance(Signature.ALG_RSA_SHA_PKCS1, false);
JCSystem.requestObjectDeletion();
//Initializ the Signature object.
rsaSignature.init(key, Signature.MODE_SIGN);
Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_INS, flags, OFF_INS, (short)3);
JCSystem.requestObjectDeletion();
}
else
{
if (flags[OFF_INS] != buffer[ISO7816.OFFSET_INS]
|| (flags[OFF_P1] & 0x7f) != (buffer[ISO7816.OFFSET_P1] & 0x7f)
|| (short)(flags[OFF_P2] & 0xff) != (short)((buffer[ISO7816.OFFSET_P2] & 0xff) - 1))
{
Util.arrayFillNonAtomic(flags, (short)0, (short)flags.length, (byte)0);
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
}
flags[OFF_P2] ++;
}
if (hasMoreCmd)
{
// Accumulates a signature of the input data.
rsaSignature.update(buffer, ISO7816.OFFSET_CDATA, len);
}
else
{
//Generates the signature of all input data.
short ret = rsaSignature.sign(buffer, ISO7816.OFFSET_CDATA, len, buffer, (short)0);
Util.arrayFillNonAtomic(flags, (short)0, (short)flags.length, (byte)0);
apdu.setOutgoingAndSend((short)0, ret);
}
}
//RSA Signature and Verify
private void rsaVerify(APDU apdu, short len)
{
byte[] buffer = apdu.getBuffer();
if (rsaPubKeyLen == 0)
{
ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
}
boolean hasMoreCmd = (buffer[ISO7816.OFFSET_P1] & 0x80) != 0;
short resultLen = 0;
short offset = ISO7816.OFFSET_CDATA;
short modLen = rsaPubKeyLen > 256 ? (short)256 : (short)128;
if (buffer[ISO7816.OFFSET_P2] == 0) //first block
{
Key key;
// Create uninitialized public keys for signature algorithms.
key = KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, (short)(modLen * 8), false);
//Sets the modulus value of the key.
((RSAPublicKey)key).setModulus(rsaPubKey, (short)0, modLen);
//Sets the public exponent value of the key.
((RSAPublicKey)key).setExponent(rsaPubKey, modLen, (short)(rsaPubKeyLen - modLen));
//Create a ALG_RSA_SHA_PKCS1 object instance.
rsaSignature = Signature.getInstance(Signature.ALG_RSA_SHA_PKCS1, false);
JCSystem.requestObjectDeletion();
//Initializes the Signature object with the appropriate Key.
rsaSignature.init(key, Signature.MODE_VERIFY);
Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_INS, flags, OFF_INS, (short)3);
Util.setShort(flags, OFF_LEN, (short)0);
JCSystem.requestObjectDeletion();
}
else
{
if (flags[OFF_INS] != buffer[ISO7816.OFFSET_INS]
|| (flags[OFF_P1] & 0x7f) != (buffer[ISO7816.OFFSET_P1] & 0x7f)
|| (short)(flags[OFF_P2] & 0xff) != (short)((buffer[ISO7816.OFFSET_P2] & 0xff) - 1))
{
Util.arrayFillNonAtomic(flags, (short)0, (short)flags.length, (byte)0);
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
}
flags[OFF_P2] ++;
}
short sigLen = Util.getShort(flags, OFF_LEN);
if (sigLen < modLen)
{
short readLen = (short)(modLen - sigLen);
if (readLen > len)
{
readLen = len;
}
Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_CDATA, tempBuffer, sigLen, readLen);
sigLen += readLen;
len -= readLen;
Util.setShort(flags, OFF_LEN, sigLen);
offset += readLen;
}
if (hasMoreCmd)
{
if (len > 0)
{
//Accumulates a signature of the input data.
rsaSignature.update(buffer, offset, len);
}
}
else
{
if (sigLen != modLen)
{
Util.arrayFillNonAtomic(flags, (short)0, (short)flags.length, (byte)0);
ISOException.throwIt(ISO7816.SW_WRONG_DATA);
}
//Verify the signature of all/last input data against the passed in signature.
boolean ret = rsaSignature.verify(buffer, offset, len, tempBuffer, (short)0, sigLen);
Util.arrayFillNonAtomic(flags, (short)0, (short)flags.length, (byte)0);
buffer[(short)0] = ret ? (byte)1 : (byte)0;
apdu.setOutgoingAndSend((short)0, (short)1);
}
}
}
The test script of RSA is as follows: [[https://javacardos.com/javacardforum/download/file.php?id=547 |Download Test Script]]
//The test script of RSA
//Copyright(C) JavaCardOS Technologies Co., Ltd. All rights reserved.
//RSA Select Applet
00A404000B4A617661436172644F530300;
//RSA
//generate-keypair-1024 in card
80300000;
//get Publck Key - N
80310000;
//get Publck Key - E The data of response APDU command is 4 bytes, such as 01 00 01 90 00
80310100;
//get RSA Private Key - N
80320000;
//get RSA Private Key - D
80320100;
//RSA encrypt - 2048
//set RSA Public key 2048bit
80338100C8C24A7A902FA8983CBBAB3F690528F86D5591496374E6B3B4EC47CA644CB55F204C4D5EEBBD9D236B6F74AC209738CB294A7C26F1251D838EDF8ADAF3E287743A5D7C65BBBCFE4A22BEB06D0D65DAFE1D1217E8AB9E5B7AA0D3883A4D6D2F5BE5B295AE6CD495D45D4452876F573C3EDD402E3BA998EC1078F662027A5F7BCE949C3B3F0309B1BE5EE9AAE720C97C2C2F0CBCD298E3FC049A457513DCCC70BDA06949011BC1F0DB6F499685377DF87B3E66890FB4C0CC09CF12DB338394C44E324D0F1931D623C035;
803301013BD00F518C353243CD22A3239811928163DCDA1352044AE4E15F10FEF648E98AE88B7BBA89F29C7EFAE5EE04E299F90F52FEE263AF2DA885FB010001;
//set RSA Private Key 2048bit
80348100C8C24A7A902FA8983CBBAB3F690528F86D5591496374E6B3B4EC47CA644CB55F204C4D5EEBBD9D236B6F74AC209738CB294A7C26F1251D838EDF8ADAF3E287743A5D7C65BBBCFE4A22BEB06D0D65DAFE1D1217E8AB9E5B7AA0D3883A4D6D2F5BE5B295AE6CD495D45D4452876F573C3EDD402E3BA998EC1078F662027A5F7BCE949C3B3F0309B1BE5EE9AAE720C97C2C2F0CBCD298E3FC049A457513DCCC70BDA06949011BC1F0DB6F499685377DF87B3E66890FB4C0CC09CF12DB338394C44E324D0F1931D623C035;
80348101C8D00F518C353243CD22A3239811928163DCDA1352044AE4E15F10FEF648E98AE88B7BBA89F29C7EFAE5EE04E299F90F52FEE263AF2DA885FB8FFFDD79122995B3A5B0558E03CA0ADE1606596B42505EAEE0549117E796A18B71A202911C76E3B293E7D84F4B65478631F3747553F9DCF0BE93CE95E5B659198D024EF06731DD468528839AE37BFCCD1504ED688D34E5DD5F0A5F93D531BD9DAFCFAA7874D31B9388B8D6325A68877B3A3F49EEF82D364440F001402B96BA533404EB121B0E6604B0678A5AAA100C56;
8034010270B3ECD17EDDCB4F5BC71BBE1E7A405679380E6D9A777AA8452A2DE5F1FECAA09ACEA4A02940A14F28C7CBF7EE0A1A1519B58ED3161A590BC66B82C13CD43C484AD2D77E8301629F9B1EB4C7505C5A7B46C0171668990775FDF2E62BB695DF424130727CD0ACC61943FE8EF513A0AC9BD1;
// RSA Signature SHA1 &2048bit
//expect:75A78094B3A63715B85327C2839CE1E1831C39B740DDB9F67F27AEBC540DB94BF95C7DEE200AC60A2BF0F64B9F4772A163E036D8DC8E865EFD5B3E7A9442BDF43BA86660D305C15DB2A3A8F5F6E3D1F50E234B193DD787B89E3EDF045E280285474A027E8985F45D883E14593D91CF79017E3AEBCF8A155DD0B5C85BE0A4301D0F80C99A15D154DF54FFE8EB603DA85184386452ABB47C985EF1D5390FF74516A7F0B8A05380DD186AA6FA584287F8AAE6421D98AD84601D734EA008CE0B37FF574BD1C9FE948DABF0A78F4E691FC0A9CAE6DC1288BD2E295CFB31443F09AF16B5D66886BA7AEF2FF272E443ADBA47798F76EF6B4594CEC399526F21236AD3A29000
8035000080C05DECFACFC439CA7AB1EC89F554FF982FB7E8E95FFFC863DFEFE5883EAD5A5591C90D69097BE26ACB2C2EA5FB386FF41AF33AE593EA7418906BC5E912474A4F41D68A04013CACE7C5EBE79EA869397FBA545CB197A3F68E3F2E104C2171C9DD798C4B13DC8FFF0811F5FDBD236EC701B4E2369594B2B48D9341ACF993B33FC5;
//RSA Verify 2048bit
80368000C875A78094B3A63715B85327C2839CE1E1831C39B740DDB9F67F27AEBC540DB94BF95C7DEE200AC60A2BF0F64B9F4772A163E036D8DC8E865EFD5B3E7A9442BDF43BA86660D305C15DB2A3A8F5F6E3D1F50E234B193DD787B89E3EDF045E280285474A027E8985F45D883E14593D91CF79017E3AEBCF8A155DD0B5C85BE0A4301D0F80C99A15D154DF54FFE8EB603DA85184386452ABB47C985EF1D5390FF74516A7F0B8A05380DD186AA6FA584287F8AAE6421D98AD84601D734EA008CE0B37FF574BD1C9FE948DAB;
//expect:019000
80360001B8F0A78F4E691FC0A9CAE6DC1288BD2E295CFB31443F09AF16B5D66886BA7AEF2FF272E443ADBA47798F76EF6B4594CEC399526F21236AD3A2C05DECFACFC439CA7AB1EC89F554FF982FB7E8E95FFFC863DFEFE5883EAD5A5591C90D69097BE26ACB2C2EA5FB386FF41AF33AE593EA7418906BC5E912474A4F41D68A04013CACE7C5EBE79EA869397FBA545CB197A3F68E3F2E104C2171C9DD798C4B13DC8FFF0811F5FDBD236EC701B4E2369594B2B48D9341ACF993B33FC5;
//RSA -2048 encrypt
80378800643E78AA266AA64FED361D907305CD70438AA5F0E674AAD69E5146AD57ACE20DDA79B71C44B4E055B644B4884F25542257C680AFBDB0AF9DC4695E43F998A8226869176A0F33BAC09359773E326EA3BBF99CD04D3746E60D31EC761A174F4AE137D6419B71;
8037880164315A16EB3C0EB90626E3C390D23ED33B7D78AD4D3E78AA266AA64FED361D907305CD70438AA5F0E674AAD69E5146AD57ACE20DDA79B71C44B4E055B644B4884F25542257C680AFBDB0AF9DC4695E43F998A8226869176A0F33BAC09359773E326EA3BBF9;
//expect:50237853BC0EBF1537BACA8022795F75FBCF4DDAF18FB99464F68E2E29D7023159BE25219D4493EADB048811304301507EEECEEC0056665E77960D338B4AB52B00B2235E2CF390B1BFDB77716C82493D719228385B707D56DAB30C06661218F162181E90094E32C5D1879F1401B3B17CE961297977CA7C97B2C10FC0FF104780A360150C797EDFA60677C200AA78F3EDCC99FDA1BCE14961A65C4F41552EE575EFB07F4074C8071F80AE083F1AA2BFDB0007A824A7828BBFB583D96B6D6EAD84F9124CA9B7A41EC522630C605886E8EAD146A6C837ED88BC39D779541B5586B518F20554FF8D1496B9EB17A3954FBCB9F3E7C5B6F01DA8C973E4B41A96B32F5C9000
80370802389CD04D3746E60D31EC761A174F4AE137D6419B71315A16EB3C0EB90626E3C390D23ED33B7D78AD4DA2353B9A1AF1D2AFA2353B9A1AF1D2AF;
//RSA -2048 decrypt
80378900C850237853BC0EBF1537BACA8022795F75FBCF4DDAF18FB99464F68E2E29D7023159BE25219D4493EADB048811304301507EEECEEC0056665E77960D338B4AB52B00B2235E2CF390B1BFDB77716C82493D719228385B707D56DAB30C06661218F162181E90094E32C5D1879F1401B3B17CE961297977CA7C97B2C10FC0FF104780A360150C797EDFA60677C200AA78F3EDCC99FDA1BCE14961A65C4F41552EE575EFB07F4074C8071F80AE083F1AA2BFDB0007A824A7828BBFB583D96B6D6EAD84F9124CA9B7A41EC5;
803709013822630C605886E8EAD146A6C837ED88BC39D779541B5586B518F20554FF8D1496B9EB17A3954FBCB9F3E7C5B6F01DA8C973E4B41A96B32F5C;
//2048-CRT
//set public Key RSA CRT-2048
80338100C8AF536A413B40AD9D363E2A8192986A4F054C1C4BE30D47EDD76A32D1AA7EE9A9F91E5BA81D20819B5A5293D8C39F8549E3695E13BE12B19405110D2FBED0E393016D231B4201408CDE963377F802579313ECBBB49320B09D8216374EC50A70D70AE1DE5E0E05AD1B7A1F57638674309FB544EB35CBB5535D3C4C727FF633C6CFFF44F0E141B38074B5C92EF26A57B173F3DDE85C5D4F0B2A3F06E4F1961BB07712D9D0B901EDC8CEA67AEDDBBA0C7EA08857B6934168AF893E7F419DE51E45EEF8272274D85F89D8;
803301013B95E867318D75FADC8AA35FC053B3E02EABA50B2D7B61F0F3EB7A4BCE1DDB43B6887C93580F8BAED5B5236EC69A7D1C42CCE978A98160AC51010001;
//set private Key RSA CRT-2048
8034C100C8E7F037F8E89DF110BA672C16BDE2930EA223E2CF63FC030650DCA03289DD4FBE1056975FCB4566822E18C9A22437FA8685C7978187E1A72860E4BE19BEAA4A88A9C1A64F3186E39E6618039418528006281ADD687842CF8D27198C6D1E16166A4CE5A983263A776A5DCA829FC2F76B1A9369F5B5B2A62382593E8E00B3D36795C183B0BEB5D5171EC3DC28BD4BFE637F76AB6874EB6E7BCDAC5F782C7FD38C3A71394FA503DA1519413D60577AD831257E8C77356952B9F1BDF909FADC609F05002A532D6FFBEBAD;
8034C101C84E68A2FF5914E2A331CFC001586F74ACE4FFD23C2FB584B175070E2C3AC414259DC831536116BA669DB636FA82E65CE1C2D873E2B40552CDCAEDFEE1BCFBF77E31D1F82908A8B54CE471F58FF3B0B79EB00DE1D195033D52EA0DA48401961227A4D48E488081701DF08997D4C29BEAD4BF2F3BD1AB9431AA9775ED5A63958984BA30868AAE23BD2BBB1815764F5896BFD5AEC2FF2B08D23A7EC18D358C3D2E99798E0C9DABDD145A3F7E7FC741097762853E882DFCFEF0165153C60C9D646456FD45980C11611537;
8034C102C8985B04F8BB926E6D14858816B088CDA15391495AE9A7D81DE594037FBB3B75B91DC7B2B454B5272BDD6DD7CAA73487160C903E849E55321324B15EEFDAE772C4B5E355A78023CD2B2D7EA3E2CF1DE7963688813BDF77B8CFAF3B873A05DF43CAF9CDB9BE9079F91187C18B816B8CC151720904B29DBC80EF6443ECCE00665C83D48E95F3B0795F0388BC89B8CF6AFE8BC2051FE6022B159BD8522EDFDE48604DD4113A7E831E78ECF82235BB5AFAA53AF129FFF21AB7BDDE57025A6B57377386EFE7992231D71C4E;
803441032876743A93B519A6A45C6E57EFC081A3CDB6F92DF81092B02DA06AAC635B981CCBFD62488823C5AF51;
//sign RSA CRT-2048
80358000C8111111413B40AD9D363E2A8192986A4F054C1C4BE30D47EDD76A32D1AA7EE9A9F91E5BA81D20819B5A5293D8C39F8549E3695E13BE12B19405110D2FBED0E393016D231B4201408CDE963377F802579313ECBBB49320B09D8216374EC50A70D70AE1DE5E0E05AD1B7A1F57638674309FB544EB35CBB5535D3C4C727FF633C6CFFF44F0E141B38074B5C92EF26A57B173F3DDE85C5D4F0B2A3F06E4F1961BB07712D9D0B901EDC8CEA67AEDDBBA0C7EA08857B6934168AF893E7F419DE51E45EEF8272274D85F89D8;
//expect: 7008B8ACE976C4024F5148DCCF30DE9CEF6D330FE803B8516A8099CF795C9C57EF9092F1BA7CB75018BF0A7378062FC6D23208CE916F55E0BA471A977CE86FB3E6AAD1282E33E8E5B8FCCF31B2FF689127C292F9B8CC0F07DDDA17A4B870510F6E19187184C3749D1CA957AB2CDE752D459C3D9427A16168BDF1705536E87A97FAAEC1485F90857A0EB2A8DAF5EEDD59F8285D2E65B8D4DEB24EE59A72645B263D02D604EEE03F93EC04EDC8CBC08619E863B2C4A521244A101C6D2EA3E789F1616CE2DFA11190A6C928C0BE4117DF4555DEA280645454CFD755C3B547813FC46667C96223EC5458A9FCB51BBF9524183F0CF3A00A89B812F597CC7285573E7D9000
803500013895E867318D75FADC8AA35FC053B3E02EABA50B2D7B61F0F3EB7A4BCE1DDB43B6887C93580F8BAED5B5236EC69A7D1C42CCE978A98160AC51;
//verify RSA CRT-2048
80368000C87008B8ACE976C4024F5148DCCF30DE9CEF6D330FE803B8516A8099CF795C9C57EF9092F1BA7CB75018BF0A7378062FC6D23208CE916F55E0BA471A977CE86FB3E6AAD1282E33E8E5B8FCCF31B2FF689127C292F9B8CC0F07DDDA17A4B870510F6E19187184C3749D1CA957AB2CDE752D459C3D9427A16168BDF1705536E87A97FAAEC1485F90857A0EB2A8DAF5EEDD59F8285D2E65B8D4DEB24EE59A72645B263D02D604EEE03F93EC04EDC8CBC08619E863B2C4A521244A101C6D2EA3E789F1616CE2DFA11190A6;
80368001C8C928C0BE4117DF4555DEA280645454CFD755C3B547813FC46667C96223EC5458A9FCB51BBF9524183F0CF3A00A89B812F597CC7285573E7D111111413B40AD9D363E2A8192986A4F054C1C4BE30D47EDD76A32D1AA7EE9A9F91E5BA81D20819B5A5293D8C39F8549E3695E13BE12B19405110D2FBED0E393016D231B4201408CDE963377F802579313ECBBB49320B09D8216374EC50A70D70AE1DE5E0E05AD1B7A1F57638674309FB544EB35CBB5535D3C4C727FF633C6CFFF44F0E141B38074B5C92EF26A57B173;
//expect:01 90 00
8036000270F3DDE85C5D4F0B2A3F06E4F1961BB07712D9D0B901EDC8CEA67AEDDBBA0C7EA08857B6934168AF893E7F419DE51E45EEF8272274D85F89D895E867318D75FADC8AA35FC053B3E02EABA50B2D7B61F0F3EB7A4BCE1DDB43B6887C93580F8BAED5B5236EC69A7D1C42CCE978A98160AC51;
//encrypt RSA CRT-2048
8037C800C8111111413B40AD9D363E2A8192986A4F054C1C4BE30D47EDD76A32D1AA7EE9A9F91E5BA81D20819B5A5293D8C39F8549E3695E13BE12B19405110D2FBED0E393016D231B4201408CDE963377F802579313ECBBB49320B09D8216374EC50A70D70AE1DE5E0E05AD1B7A1F57638674309FB544EB35CBB5535D3C4C727FF633C6CFFF44F0E141B38074B5C92EF26A57B173F3DDE85C5D4F0B2A3F06E4F1961BB07712D9D0B901EDC8CEA67AEDDBBA0C7EA08857B6934168AF893E7F419DE51E45EEF8272274D85F89D8;
//expect:45ACD17EE02228DE45096F1153EF89DBF72933925A17803142A093FD2300CE27CF26A1B38F402E167647035227E0CBB238208A7E166E9DD4F563E04D48C9916C6812AA40AF4715C2D70D32190BC4F9EC2DA0FAD7EFE703AE2808401AA389698BC7428E9172B868E328DA5698CACDBE489EA871225DBEBB238B015828E4B68B274175910D1F72D8D684D24A40077F3A5C75F086A57B733C74CC94C7377A8F555DCB638A4A336344D9B2D53594E7C24BF3FA85705C2E6818B9703EDBBEFDCFA2FC42D3791816025BE4D3045ABF37AEEDF290B90D93E151AFA128E50A271A19AAA7988EBBACF17D26AAF8AAEA0C027186E2715E3A850971F5F16E44DB4FFACE07D09000
803748013895E867318D75FADC8AA35FC053B3E02EABA50B2D7B61F0F3EB7A4BCE1DDB43B6887C93580F8BAED5B5236EC69A7D1C42CCE978A98160AC51;
//decrypt RSA CRT-2048
8037C900C845ACD17EE02228DE45096F1153EF89DBF72933925A17803142A093FD2300CE27CF26A1B38F402E167647035227E0CBB238208A7E166E9DD4F563E04D48C9916C6812AA40AF4715C2D70D32190BC4F9EC2DA0FAD7EFE703AE2808401AA389698BC7428E9172B868E328DA5698CACDBE489EA871225DBEBB238B015828E4B68B274175910D1F72D8D684D24A40077F3A5C75F086A57B733C74CC94C7377A8F555DCB638A4A336344D9B2D53594E7C24BF3FA85705C2E6818B9703EDBBEFDCFA2FC42D3791816025BE4;
//expect:111111413B40AD9D363E2A8192986A4F054C1C4BE30D47EDD76A32D1AA7EE9A9F91E5BA81D20819B5A5293D8C39F8549E3695E13BE12B19405110D2FBED0E393016D231B4201408CDE963377F802579313ECBBB49320B09D8216374EC50A70D70AE1DE5E0E05AD1B7A1F57638674309FB544EB35CBB5535D3C4C727FF633C6CFFF44F0E141B38074B5C92EF26A57B173F3DDE85C5D4F0B2A3F06E4F1961BB07712D9D0B901EDC8CEA67AEDDBBA0C7EA08857B6934168AF893E7F419DE51E45EEF8272274D85F89D895E867318D75FADC8AA35FC053B3E02EABA50B2D7B61F0F3EB7A4BCE1DDB43B6887C93580F8BAED5B5236EC69A7D1C42CCE978A98160AC519000
8037490138D3045ABF37AEEDF290B90D93E151AFA128E50A271A19AAA7988EBBACF17D26AAF8AAEA0C027186E2715E3A850971F5F16E44DB4FFACE07D0;