I have an issue with obtaining the RSA public key modulus with my Java Card applet implementation: The command SEND_PUB (see case statement case SEND_PUB) for sending the public 512 bits RSA key returns the status word 6F 00. What could be wrong with my implementation?
public class crypto extends Applet {
private static final boolean NO_EXTERNAL_ACCESS = false;
private static byte[] file=new byte[128];
private static byte[] SignedFile=new byte[20];
private static RSAPublicKey p;
private static RSAPublicKey publicKey;
private static RSAPrivateKey privateKey;
private static KeyPair keyPair;
Signature sig;
private final static byte ALOC= 0x07; //vérifier le code PIN
private final static byte INS_PIN= 0x01; //vérifier le code PIN
private final static byte INS_PUK= 0x02; //vérifier le code PUK
private final static byte UPD_PIN= 0x03; //modifier le code PIN
private final static byte RCV_FILE= 0x04; //recvoir le fichier
private final static byte SIGNATURE= 0x05; //Récupérer la clé privée
private final static byte SEND_PUB= 0x06; //envoyer la la clé publique
public static OwnerPIN pin,puk;
public static void install(byte[] bArray, short bOffset, byte bLength) {
new crypto();
}
protected crypto() {
register();
puk = new OwnerPIN(nbre_tentative, length);
puk.update(code_puk, (short) 0, length);
pin = new OwnerPIN(nbre_tentative, length);
pin.update(code_pin, (short) 0, length);
// publicKey = (RSAPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC,KeyBuilder.LENGTH_RSA_512,true);
// keyPair = new KeyPair(KeyPair.ALG_RSA, (short) publicKey.getSize());
// publicKey = (RSAPublicKey) keyPair.getPublic();
KeyPair rsa_KeyPair = new KeyPair(KeyPair.ALG_RSA_CRT, KeyBuilder.LENGTH_RSA_512);
rsa_KeyPair.genKeyPair();
RSAPublicKey p = (RSAPublicKey) rsa_KeyPair.getPublic();
//RSAPrivateKey rsa_PrivateCrtKey 0= (RSAPrivateKey) rsa_KeyPair.getPrivate();
// cipherRSA = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);
}
public void process(APDU apdu) {
byte[] buffer = apdu.getBuffer();
if(selectingApplet())
return;
if(buffer[ISO7816.OFFSET_CLA] != CLA)
ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
switch (buffer[ISO7816.OFFSET_INS])
{
case SEND_PUB :
//this is to send the modulus
p.getModulus(buffer, ISO7816.OFFSET_CDATA);
apdu.setOutgoing();
apdu.setOutgoingLength((short) 64);
apdu.sendBytesLong(buffer, ISO7816.OFFSET_CDATA, (short) 64);
case SIGNATURE :
Signature s = Signature.getInstance(Signature.ALG_RSA_SHA_PKCS1, false);
s.init(privateKey, Signature.MODE_SIGN);
short sigLen = s.sign(file,(short)0, (short)file.length,SignedFile, (short)0);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
}
}
You receive status word 6F 00 because you get a NullPointerException when accessing p on the line p.getModulus(...);. The reason for this is that the instance field p was never initialized (at least not with the code that you showed in your question above) and is, consequently, null.
Note that the line
RSAPublicKey p = (RSAPublicKey) rsa_KeyPair.getPublic();
does not assign the public key object to the the instance field p but to a local variable that is also named p and, thus, hides the instance field.
Related
Currently working on upgrading from Java 13 to Java 16 and using the following updated dependencies for a cryptography library performing encryption/decryption:
BouncyCastle 1.69
Google Tink 1.6.1
=================================
Crypto Library class:
package com.decryption.test;
#NoArgsConstructor
#Service
public class CryptoLib {
protected static final String PROTOCOL_VERSION = "ECv2";
protected final String DEV_ROOT_PUB_KEY = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESXy7kqanQwAM/HBTcV0MVtgQkKLY6UVqP3Z/vdlxiRgFqnc9dZUSD8muUpgeZDD05lM68qoI31mbeX9c8P/9Uw==";
protected static final String MERCHANT_ID_PREFIX = "merchant:";
protected static final String HMAC_SHA256_ALGO = "HmacSha256";
protected static final int HMAC_SHA256_ALGO_SIZE = 256 / 8; // 256 bit key size -> 32 bytes
protected static final String AES_CTR_ALGO = "AES/CTR/NoPadding";
protected static final int AES_CTR_ALGO_SIZE = 256 / 8; // 256 bit key size -> 32 bytes
protected static final byte[] HKDF_EMPTY_SALT = new byte[0];
protected static final byte[] AES_CTR_ZERO_IV = new byte[16];
protected final static String ASYMMETRIC_KEY_TYPE = "EC";
protected static final String SENDER_ID = "Google";
protected static final String SECURITY_PROVIDER = "BC";
protected static final String EC_CURVE = "prime256v1";
protected static final String CSR_SIGNATURE = "SHA256WITHECDSA";
protected static final String SIGNATURE_ALGORITHM = "SHA256withECDSA";
#Autowired
private ProfileManager profileManager;
#PostConstruct
public void loadKeys() {
final StringBuilder message = new StringBuilder("Loading Google pay signing keys. ");
if (this.profileManager.isProfileActive("dev")) {
message.append("Profile DEV is active. Only test rig encrypted payloads supported.");
} else {
final GooglePaymentsPublicKeysManager googleKeyManager = this.getGoogleKeyManager();
googleKeyManager.refreshInBackground();
}
System.out.println(message);
}
private static String getMerchantIdComponent(final String merchantId) {
return merchantId.startsWith(MERCHANT_ID_PREFIX) ?
merchantId : MERCHANT_ID_PREFIX + merchantId;
}
private GooglePaymentsPublicKeysManager getGoogleKeyManager() {
return profileManager.isProfileActive("prod") ?
GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION :
GooglePaymentsPublicKeysManager.INSTANCE_TEST;
}
public String decrypt(final GoogleDecryptRequest decryptRequest) throws Exception {
try {
final PaymentMethodTokenRecipient.Builder builder = new PaymentMethodTokenRecipient.Builder()
.protocolVersion(PROTOCOL_VERSION)
.recipientId(getMerchantIdComponent(decryptRequest.getMerchantId()));
if (decryptRequest.getPrivateKey() != null) {
builder.addRecipientPrivateKey(decryptRequest.getPrivateKey());
}
//Add all our retired keys to the list.
for (final ECPrivateKey ecPrivateKey : decryptRequest.getOldPrivateKeys()) {
builder.addRecipientPrivateKey(ecPrivateKey);
}
if (this.profileManager.isProfileActive("dev")) {
builder.addSenderVerifyingKey(DEV_ROOT_PUB_KEY);
} else {
builder.fetchSenderVerifyingKeysWith(getGoogleKeyManager());
}
return builder.build()
.unseal(decryptRequest.getEncryptedMessage());
} catch (final Exception e) {
final String errMsg = MessageFormat.format("GooglePay Decryption failed for Google merchant ID [{0}]", decryptRequest.getMerchantId());
throw new Exception(errMsg, e);
}
}
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
static class GoogleEncryptRequest implements EncryptRequest<ECPublicKey> {
/**
* Merchant public key
*/
private ECPublicKey publicKey;
private String algorithm = "EC";
/**
* Google merchantID. If the merchant ID is xxx, this value should be either
* <ul>
* <li>xxx</li>
* <li>merchant:xxx</li>
* </ul>
*/
private String merchantId;
/**
* Message to encrypt
*/
private String message;
}
public EncryptResponse encrypt(final GoogleEncryptRequest encryptRequest) throws Exception {
final String DEV_ROOT_PRIV_KEY = "MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg3lO35/XiUzEooUJlLKEd0BJmoLgeTvkFSm/b4wMrgdWgCgYIKoZIzj0DAQehRANCAARJfLuSpqdDAAz8cFNxXQxW2BCQotjpRWo/dn+92XGJGAWqdz11lRIPya5SmB5kMPTmUzryqgjfWZt5f1zw//1T";
final String DEV_ROOT_PUB_KEY = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESXy7kqanQwAM/HBTcV0MVtgQkKLY6UVqP3Z/vdlxiRgFqnc9dZUSD8muUpgeZDD05lM68qoI31mbeX9c8P/9Uw==";
final GoogleEncryptResponse response = new GoogleEncryptResponse();
try {
//STEP 1: Generate all keys.
// Generate root keypair. Used to sign intermediate. jsonPath -> $.intermediateSigningKey.signatures
final PrivateKey rootPrivKey = CryptoUtility.getPrivateFromPKCS8Base64(DEV_ROOT_PRIV_KEY, ASYMMETRIC_KEY_TYPE);
final PublicKey rootPubKey = CryptoUtility.getPublicFromBase64(DEV_ROOT_PUB_KEY, ASYMMETRIC_KEY_TYPE); //equiv to the verifying key from the test/prod APIs
// Generate intermediate keypair. Used to sign whole payload. jsonPath -> $.signature
final KeyPair intermediateKeyPair = generateKeyPair();
final PrivateKey intermediatePrivKey = intermediateKeyPair.getPrivate();
// Convert to uncompressed point and B64 encode. Goes to $.intermediateSigningKey.signedKey.keyValue
final PublicKey intermediatePubKey = intermediateKeyPair.getPublic();
// Generate ephemeral pub key. Used to create a shared secret to decrypt the signed message
final KeyPair ephemeralKeyPair = generateKeyPair();
final PrivateKey ephemeralPrivKey = ephemeralKeyPair.getPrivate();
// Convert to uncompressed point and B64 encode. Goes to $.signedMessage.ephemeralPublicKey
final PublicKey ephemeralPubKey = ephemeralKeyPair.getPublic();
final byte[] ephemeralPubKeyBytes = getUncompressedPoint(ephemeralPubKey);
//Parse merchant public key
final ECPublicKey merchantPublicKey = encryptRequest.getPublicKey();
/*
STEP 2: Sign the intermediate key. This step will generate the intermediateSigningKey block in the json
ECDSA(length_of_sender_id || sender_id || length_of_protocol_version || protocol_version || length_of_signed_key || signed_key)
*/
final long keyExpiry = Instant.now().plus(365, ChronoUnit.DAYS).toEpochMilli();
final String intermediatePubKeyB64 = java.util.Base64.getEncoder().encodeToString(intermediatePubKey.getEncoded());
response.setIntermediateSigningKey(keyExpiry, intermediatePubKeyB64);
final byte[] senderComponent = getSignatureBytesForComponent(SENDER_ID);
final byte[] protocolVersionComponent = getSignatureBytesForComponent(PROTOCOL_VERSION);
final String signingKeyJson = new Gson().toJson(response.getIntermediateSigningKey().getSignedKey());
final byte[] signingKeyComponent = getSignatureBytesForComponent(signingKeyJson);
//Assemble all components into one byte array
final byte[] intermediateSignatureComponentBytes = ByteBuffer
.allocate(senderComponent.length + protocolVersionComponent.length + signingKeyComponent.length)
.put(senderComponent)
.put(protocolVersionComponent)
.put(signingKeyComponent)
.array();
//Sign the byte array using ECDSA
final String intermediateSignature = ecdsaSignMessageB64(rootPrivKey, intermediateSignatureComponentBytes);
response.setIntermediateSigningKeySignatures(intermediateSignature);
/*
Step 3: Create the signed message. Includes tag, encrypted data and ephemeralPubKey
*/
//Step 3a: Generate shared secret. Used for symmetric encryption
final byte[] sharedSecret = EllipticCurves.computeSharedSecret(
(ECPrivateKey) ephemeralPrivKey,
merchantPublicKey.getW());
//Step 3b: Generate HMAC key and symmetric key using shared secret
final byte[] eciesKey = Hkdf.computeEciesHkdfSymmetricKey(
ephemeralPubKeyBytes,
sharedSecret,
HMAC_SHA256_ALGO,
HKDF_EMPTY_SALT,
SENDER_ID.getBytes(StandardCharsets.UTF_8),
HMAC_SHA256_ALGO_SIZE + AES_CTR_ALGO_SIZE //256 bit aes key and 256 bit hmac key
);
final byte[] aesKey = Arrays.copyOf(eciesKey, AES_CTR_ALGO_SIZE); // [0,32] bytes
final byte[] hmacKey = Arrays.copyOfRange(eciesKey, HMAC_SHA256_ALGO_SIZE, HMAC_SHA256_ALGO_SIZE * 2); //[32,64]
//Step 3c: Encrypt data
final Cipher cipher = EngineFactory.CIPHER.getInstance(AES_CTR_ALGO);
cipher.init(
Cipher.ENCRYPT_MODE,
new SecretKeySpec(aesKey, "AES"),
new IvParameterSpec(AES_CTR_ZERO_IV));
final byte[] cipherBytes = cipher.doFinal(encryptRequest.getMessage().getBytes(StandardCharsets.UTF_8));
//Step 3d: Compute HMAC tag
final SecretKeySpec secretKeySpec = new SecretKeySpec(hmacKey, HMAC_SHA256_ALGO);
final Mac mac = EngineFactory.MAC.getInstance(HMAC_SHA256_ALGO);
mac.init(secretKeySpec);
final byte[] hmacBytes = mac.doFinal(cipherBytes);
//Step 3e: Populate data in response. $.signedMessage
final String ephemeralPublicKeyB64 = java.util.Base64.getEncoder().encodeToString(ephemeralPubKeyBytes);
final String hmacB64 = java.util.Base64.getEncoder().encodeToString(hmacBytes);
final String cipherB64 = java.util.Base64.getEncoder().encodeToString(cipherBytes);
response.setSignedMessage(cipherB64, hmacB64, ephemeralPublicKeyB64);
/*
Step 4: Create $.signature using intermediate priv and the serialized signed key
ECDSA(length_of_sender_id || sender_id || length_of_recipient_id || recipient_id || length_of_protocolVersion || protocolVersion || length_of_signedMessage || signedMessage)
*/
final String merchantIdComponentString = getMerchantIdComponent(encryptRequest.getMerchantId());
final byte[] merchantComponent = getSignatureBytesForComponent(merchantIdComponentString);
;
final String signedMessageJson = new Gson().toJson(response.getSignedMessage());
final byte[] signedMessageComponent = getSignatureBytesForComponent(signedMessageJson);
//Assemble all components into one byte array
final int signatureComponentLength = senderComponent.length + merchantComponent.length + protocolVersionComponent.length + signedMessageComponent.length;
final byte[] signatureComponentBytes = ByteBuffer
.allocate(signatureComponentLength)
.put(senderComponent)
.put(merchantComponent)
.put(protocolVersionComponent)
.put(signedMessageComponent)
.array();
final String signatureB64 = ecdsaSignMessageB64(intermediatePrivKey, signatureComponentBytes);
response.setSignature(signatureB64);
} catch (final Exception e) {
throw new Exception("Encryption failed: " + e);
}
return response;
}
static byte[] getSignatureBytesForComponent(final String component) {
final byte[] componentLengthBytes = ByteBuffer
.allocate(4)
.order(ByteOrder.LITTLE_ENDIAN)
.putInt(component.length())
.array();
final byte[] componentBytes = ByteBuffer
.wrap(component.getBytes(StandardCharsets.UTF_8))
.array();
return ByteBuffer.allocate(componentLengthBytes.length + componentBytes.length)
.put(componentLengthBytes)
.put(componentBytes)
.array();
}
public static KeyPair generateKeyPair() throws Exception {
try {
final KeyPairGenerator kpg = KeyPairGenerator.getInstance(ASYMMETRIC_KEY_TYPE, SECURITY_PROVIDER);
kpg.initialize(generateECParameterSpec());
return kpg.generateKeyPair();
} catch (final GeneralSecurityException e) {
System.out.println("Failed to generate ECC KeyPair. " + e);
throw new Exception("Failed to generate ECC KeyPair.");
}
}
protected static ECNamedCurveSpec generateECParameterSpec() {
final ECNamedCurveParameterSpec bcParams = ECNamedCurveTable.getParameterSpec(EC_CURVE);
final ECNamedCurveSpec params = new ECNamedCurveSpec(bcParams.getName(), bcParams.getCurve(),
bcParams.getG(), bcParams.getN(), bcParams.getH(), bcParams.getSeed());
return params;
}
private static String ecdsaSignMessageB64(final PrivateKey privateKey, final byte[] messageToSign) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
final Signature intermediateECDSASignature = Signature.getInstance(SIGNATURE_ALGORITHM);
intermediateECDSASignature.initSign(privateKey);
intermediateECDSASignature.update(messageToSign);
final byte[] intermediateSignatureBytes = intermediateECDSASignature.sign();
return java.util.Base64.getEncoder().encodeToString(intermediateSignatureBytes);
}
public static byte[] getUncompressedPoint(final PublicKey publicKey) {
final java.security.interfaces.ECPublicKey pub = (java.security.interfaces.ECPublicKey) publicKey;
return pub.getEncoded();
}
#Component
public class ProfileManager {
#Autowired
private Environment environment;
#PostConstruct
public void setup() {
System.out.println(this.getActiveProfiles());
}
public boolean isProfileActive(final String profile) {
for (final String activeProfile : this.environment.getActiveProfiles()) {
if (activeProfile.equalsIgnoreCase(profile)) {
return true;
}
}
return false;
}
public List<String> getActiveProfiles() {
return List.of(this.environment.getActiveProfiles());
}
}
#Slf4j
#Data
#NoArgsConstructor
public class GoogleEncryptResponse implements EncryptResponse {
//These entities represent the structure provided by the GPay spec. See link in class docs
private String protocolVersion = "ECv2";
private String signature;
private IntermediateSigningKey intermediateSigningKey = new IntermediateSigningKey();
private SignedMessage signedMessage = new SignedMessage();
public void setIntermediateSigningKey(final long keyExpiration, final String keyValue) {
this.intermediateSigningKey.getSignedKey().setKeyExpiration(Long.toString(keyExpiration));
this.intermediateSigningKey.getSignedKey().setKeyValue(keyValue);
}
public void setIntermediateSigningKeySignatures(final String... signatures) {
this.intermediateSigningKey.setSignatures(signatures);
}
public void setSignedMessage(final String encryptedMessage, final String tag, final String ephemeralPublicKey) {
this.signedMessage.setEncryptedMessage(encryptedMessage);
this.signedMessage.setTag(tag);
this.signedMessage.setEphemeralPublicKey(ephemeralPublicKey);
}
#Override
public String getResult() {
Gson gson = new Gson();
JsonObject intermediateSigningKeyJson = new JsonObject();
JsonArray intermediateSigningKeyJsonSignatures = new JsonArray();
Arrays.stream(intermediateSigningKey.getSignatures())
.forEach(intermediateSigningKeyJsonSignatures::add);
intermediateSigningKeyJson.add("signatures", intermediateSigningKeyJsonSignatures);
intermediateSigningKeyJson.addProperty("signedKey", gson.toJson(getIntermediateSigningKey().getSignedKey()));
JsonObject payloadRoot = new JsonObject();
payloadRoot.addProperty("protocolVersion", protocolVersion);
payloadRoot.addProperty("signature", signature);
payloadRoot.add("intermediateSigningKey", intermediateSigningKeyJson);
payloadRoot.addProperty("signedMessage", gson.toJson(getSignedMessage()));
return getJsonElementHtmlSafe(payloadRoot);
}
#Data
#NoArgsConstructor
public static class IntermediateSigningKey {
private SignedKey signedKey = new SignedKey();
/**
* Signatures signed by the verifying key. Should be B64 encoded used SHA256 hashing with
* ECDSA
*/
private String[] signatures;
#Data
#NoArgsConstructor
static class SignedKey {
/**
* A Base64 version of key encoded in ASN.1 type.
* aka, der format. key.getEncoded()
*/
private String keyValue;
/**
* Date and time when the intermediate key expires as UTC milliseconds since epoch
*/
private String keyExpiration;
}
}
#Setter
#NoArgsConstructor
private static class SignedMessage {
/**
* A Base64-encoded encrypted message that contains payment information
*/
private String encryptedMessage;
/**
* A Base64-encoded ephemeral public key associated
* with the private key to encrypt the message in uncompressed point format
*/
private String ephemeralPublicKey;
/**
* A Base64-encoded MAC of encryptedMessage.
*/
private String tag;
}
}
public interface EncryptResponse {
String getResult();
}
public interface EncryptRequest<K extends PublicKey> {
/**
* Public key used to encrypt
* #return
*/
K getPublicKey();
void setPublicKey(K publicKey);
String getAlgorithm();
/**
* Set the PublicKey object using a base64 encoded key.
* #param base64EncodedKey
*/
default void setPublicKey(final String base64EncodedKey) throws Exception {
PublicKey publicKey = CryptoUtility.getPublicFromBase64(base64EncodedKey, getAlgorithm());
setPublicKey((K) publicKey);
}
/**
* Message to encrypt
* #return
*/
String getMessage();
}
public static String getJsonElementHtmlSafe(JsonElement element) {
try {
StringWriter stringWriter = new StringWriter();
JsonWriter jsonWriter = new JsonWriter(stringWriter);
jsonWriter.setLenient(false);
jsonWriter.setHtmlSafe(true); //Ensures '=' will appear as \u003d
Streams.write(element, jsonWriter);
return stringWriter.toString();
} catch (IOException e) {
return null;
}
}
#Builder
#Data
#NoArgsConstructor
#AllArgsConstructor
public static class GoogleDecryptRequest implements DecryptRequest<ECPrivateKey> {
/**
* This represents the encrypted value
*/
private String encryptedMessage;
/**
* The current active private key
*/
private ECPrivateKey privateKey;
/**
* These privateKey represents a private key that may have been rotated recently.
* May be empty
*/
#Singular
private List<ECPrivateKey> oldPrivateKeys = new ArrayList<>();
/**
* Corresponds to the GPay console merchant id
* Value must be prefixed with merchant:<br/>
* For example: merchant:1234345345345
*/
private String merchantId;
private String algorithm = "EC";
public void addOldPrivateKey(final ECPrivateKey privateKey) {
this.oldPrivateKeys.add(privateKey);
}
public static class GoogleDecryptRequestBuilder {
public GoogleDecryptRequestBuilder encryptedMessage(final String encryptedMessage) {
try {
final JsonElement jsonPayload = new JsonParser().parse(encryptedMessage);
this.encryptedMessage = getJsonElementHtmlSafe(jsonPayload);
} catch (final Exception e) {
System.out.println("Failed to parse GooglePay encrypted message. Data MUST be a valid json string." + e);
}
return this;
}
}
}
public interface DecryptRequest<K extends PrivateKey> {
/**
* Return the message that will be decrypted.
* #return
*/
String getEncryptedMessage();
/**
* Return the private key required for decryption
* #return
*/
K getPrivateKey();
void setPrivateKey(K privateKey);
String getAlgorithm();
/**
* Set the PublicKey object using a base64 encoded key.
* #param base64EncodedKey
*/
default void setPrivateKey(final String base64EncodedKey) throws Exception {
PrivateKey privateKey = CryptoUtility.getPrivateFromPKCS8Base64(base64EncodedKey, getAlgorithm());
setPrivateKey((K) privateKey);
}
}
}
CryptoUtility class:
package com.decryption.test;
public class CryptoUtility {
public static PrivateKey getPrivateFromPKCS8Base64(final String base64, final String algorithm) throws Exception {
final byte[] privateKeyBytes = Base64.decode(base64);
return getPrivateFromPKCS8Der(privateKeyBytes, algorithm);
}
public static PrivateKey getPrivateFromPKCS8Der(final byte[] privateKeyBytes, final String algorithm) throws Exception {
try {
final PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKeyBytes);
final KeyFactory kf = KeyFactory.getInstance(algorithm, CryptoLib.SECURITY_PROVIDER);
return kf.generatePrivate(spec);
} catch (final Exception e) {
throw new Exception("Failed to created Private Key." + e);
}
}
public static PublicKey getPublicFromBase64(final String base64, final String algorithm) throws Exception {
final byte[] publicKeyBytes = Base64.decode(base64);
return getPublicFromDer(publicKeyBytes, algorithm);
}
public static PublicKey getPublicFromDer(final byte[] publicKeyBytes, final String algorithm) throws Exception {
try {
final X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKeyBytes, algorithm);
final KeyFactory kf = KeyFactory.getInstance(algorithm, CryptoLib.SECURITY_PROVIDER);
return kf.generatePublic(spec);
} catch (final Exception e) {
throw new Exception("Failed to created Public Key from string value" + e);
}
}
}
JUnit class:
package com.decryption.test;
#ExtendWith(MockitoExtension.class)
public class DecryptionTest {
#InjectMocks
private CryptoLib googleECCCryptoLibrary;
#Mock
private CryptoLib.ProfileManager profileManager;
#Test
void encryptAndDecryptMockData() throws Exception {
final ECPrivateKey merchantPrivKey = (ECPrivateKey) CryptoUtility.getPrivateFromPKCS8Base64("MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgsfylDy+Q3lfR6nbipZGDZWzp6P+qiQeApJizxG/hj96gCgYIKoZIzj0DAQehRANCAAQg1SVNuof6FndzJkbPst37moW+L/36EPLiiosS5BBSsLK4q2aLxzk2M732OpDHkXTp31ZQitPDQImndaY57ZSM", "EC");
final ECPublicKey merchantPubKey = (ECPublicKey) CryptoUtility.getPublicFromBase64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEINUlTbqH+hZ3cyZGz7Ld+5qFvi/9+hDy4oqLEuQQUrCyuKtmi8c5NjO99jqQx5F06d9WUIrTw0CJp3WmOe2UjA==", "EC");
final CryptoLib.GoogleEncryptRequest encRequest = CryptoLib.GoogleEncryptRequest.builder()
.merchantId("12345678901234567890")
.message("heyhey!")
.publicKey(merchantPubKey)
.build();
final CryptoLib.EncryptResponse encResponse = googleECCCryptoLibrary.encrypt(encRequest);
final CryptoLib.GoogleDecryptRequest decryptRequest = CryptoLib.GoogleDecryptRequest.builder()
.privateKey(merchantPrivKey)
.merchantId("12345678901234567890")
.encryptedMessage(encResponse.getResult())
.build();
given(this.profileManager.isProfileActive(anyString())).willReturn(true);
final String decResponse = this.googleECCCryptoLibrary.decrypt(decryptRequest);
assertEquals(encRequest.getMessage(), decResponse);
}
}
Stacktrace:
Caused by: java.security.GeneralSecurityException: java.lang.IllegalStateException: java.security.InvalidAlgorithmParameterException: Curve not supported: secp256r1 [NIST P-256,X9.62 prime256v1] (1.2.840.10045.3.1.7)
at com.google.crypto.tink.subtle.EllipticCurves.computeSharedSecret(EllipticCurves.java:963)
ECDHKeyAgreement.engineGenerateSecret() implementation seems to be changed in Java 16 where it is now throwing:
throw new IllegalStateException(new InvalidAlgorithmParameterException("Curve not supported
as compared to the implementation in Java 13.
Any recommendations to implement encryption in JDK 16 with BouncyCastle as the provider or if it should be replaced by any other provider?
We have to build a signature string hashed using SHA-256 with random salt and using 10000 iterations. However, when I run the below code with salt, output hash value is not matching at all for the same input string. If I run the code without the salt, hashed value matches. Can someone point out the right way to add a random salt to the SHA-256 digest and get the firstValue/secondValue same below.
import java.security.MessageDigest;
import java.security.SecureRandom;
import org.springframework.security.crypto.codec.Hex;
public class someTester{
public static void main(String[] args) {
String signatureInput = "someStringWhichisSensitive";
String firstSignValue = getSignature(signatureInput);
String secondSignValue = getSignature(signatureInput);
System.out.println("firstSignValue="+firstSignValue);
System.out.println("secondSignValue="+secondSignValue);
}
private static String getSignature(String signatureInput){
MessageDigest md;
String signatureValue = null;
try {
// Create a random salt
SecureRandom sr = new SecureRandom();
byte[] bSalt = new byte[8];
sr.nextBytes(bSalt);
md = MessageDigest.getInstance("SHA-256");
// digest.update(bSalt);
byte[] bDigest = md.digest(signatureInput.getBytes());
// Iterate through 10000 times
for (int i = 0; i < 10000; i++) {
bDigest = md.digest(bDigest);
}
signatureValue = new String(Hex.encode(bDigest));
} catch (Exception e) {
System.out.println("Exception while calculating SHA-256 digest value"+e);
}
return signatureValue;
}
}
I try to generate a public/private key pair which i will use for digital signature of a JWT with jose4j. I use Elliptic Curve Digital Signature Algorithm
My problem is that i don't know how to get the parameters representing the edcsa key meaning:
crv
x
y
d
KeyPairGenerator g = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec kpgparams = new ECGenParameterSpec("secp256r1");
g.initialize(kpgparams);
KeyPair pair = g.generateKeyPair();
// Instance of signature class with SHA256withECDSA algorithm
Signature ecdsaSign = Signature.getInstance("SHA256withECDSA");
ecdsaSign.initSign(pair.getPrivate());
System.out.println("Private Keys is::" + pair.getPrivate());
System.out.println("Public Keys is::" + pair.getPublic());
JsonWebKeySet jsonWebKeySet = new JsonWebKeySet();
PrivateKey privateKey = pair.getPrivate();
JsonWebKey webKey = new JsonWebKey(privateKey) {
#Override
public String getKeyType() {
// TODO Auto-generated method stub
return "EC";
}
#Override
protected void fillTypeSpecificParams(Map<String, Object> params,
OutputControlLevel outputLevel) {
params.put("use", "sig");
params.put("key_ops", "sign");
params.put("alg", "ES256");
params.put("kid", "kukuPrivateKey");
}
};
jsonWebKeySet.addJsonWebKey(webKey);
System.out.println("aaaa"+jsonWebKeySet.toJson());
You can create a JsonWebKey directly with the public key you generated and jose4j will take care of the parameters and encoding.
KeyPairGenerator g = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec kpgparams = new ECGenParameterSpec("secp256r1");
g.initialize(kpgparams);
KeyPair keyPair = g.generateKeyPair();
PublicJsonWebKey jwk = PublicJsonWebKey.Factory.newPublicJwk(keyPair.getPublic());
jwk.setPrivateKey(keyPair.getPrivate());
jwk.setUse(Use.SIGNATURE);
System.out.println(jwk.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY));
System.out.println(jwk.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE)); // to include the private key 'd'
You can also use the EcJwkGenerator utility in jose4j to generate the key pair and wrap it in a JsonWebKey,
EllipticCurveJsonWebKey jwk = EcJwkGenerator.generateJwk(EllipticCurves.P256);
jwk.setUse(Use.SIGNATURE);
System.out.println(jwk.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY));
System.out.println(jwk.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE)); // to include the private key 'd'
After struggling with it a long time i got the following
private static String createWebKeySet() throws NoSuchAlgorithmException,
InvalidAlgorithmParameterException, InvalidKeyException {
KeyPairGenerator g = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec kpgparams = new ECGenParameterSpec("secp256r1");
g.initialize(kpgparams);
KeyPair pair = g.generateKeyPair();
// Instance of signature class with SHA256withECDSA algorithm
Signature ecdsaSign = Signature.getInstance("SHA256withECDSA");
ecdsaSign.initSign(pair.getPrivate());
System.out.println("Private Keys is::" + pair.getPrivate());
System.out.println("Public Keys is::" + pair.getPublic());
JsonWebKeySet jsonWebKeySet = new JsonWebKeySet();
final ECPrivateKey privateKey = (ECPrivateKey) pair.getPrivate();
final ECPublicKey publicKey = (ECPublicKey) pair.getPublic();
JsonWebKey privateWebKey = new JsonWebKey(privateKey) {
#Override
public String getKeyType() {
// TODO Auto-generated method stub
return "EC";
}
#Override
protected void fillTypeSpecificParams(Map<String, Object> params,
OutputControlLevel outputLevel) {
params.put("use", "sig");
params.put("key_ops", "sign");
//params.put("alg", "ES256");
params.put("kid", "kukuPrivateKey");
ECParameterSpec paramSpec = privateKey.getParams();
params.put("crv", "P-"+paramSpec.getCurve().getField().getFieldSize());
params.put("x", Base64.encode(publicKey.getW().getAffineX().toByteArray()));
params.put("y", Base64.encode(publicKey.getW().getAffineY().toByteArray()));
params.put("d",Base64.encode(privateKey.getS().toByteArray()));
}
};
jsonWebKeySet.addJsonWebKey(privateWebKey);
JsonWebKey publicWebKey = new JsonWebKey(publicKey) {
#Override
public String getKeyType() {
// TODO Auto-generated method stub
return "EC";
}
#Override
protected void fillTypeSpecificParams(Map<String, Object> params,
OutputControlLevel outputLevel) {
params.put("use", "sig");
params.put("key_ops", "verify");
//params.put("alg", "ES256");
params.put("kid", "kukuPublicKey");
ECParameterSpec paramSpec = publicKey.getParams();
params.put("crv", "P-"+paramSpec.getCurve().getField().getFieldSize());
params.put("x", Base64.encode(publicKey.getW().getAffineX().toByteArray()));
params.put("y", Base64.encode(publicKey.getW().getAffineY().toByteArray()));
}
};
jsonWebKeySet.addJsonWebKey(publicWebKey);
return jsonWebKeySet.toJson();
}
I wrote a ServerResource below with restlet-android-2.1.4. If the I set SIZE to 1024 * 12 + 485, it works. But if I change SIZE to 1024 * 12 + 486 this handle will pending.
public class DataResource extends ServerResource {
public static final int SIZE = 1024 * 12 + 485;
#Get
public Representation getResource(Representation entity) {
return new OutputRepresentation(MediaType.ALL) {
#Override
public void write(OutputStream outputStream) throws IOException {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < SIZE; i++) {
sb.append('E');
}
outputStream.write(sb.toString().getBytes());
outputStream.close();
}
};
}
}
Dive into the code, change the write function in WritableSocketChannel.java(in restlet source code) and it will work.
I don't know whether it's a bug.
from
public int write(ByteBuffer src) throws IOException {
return getWrappedChannel().write(src);
}
to
public int write(ByteBuffer src) throws IOException {
int count = 0;
while (src.hasRemaining()) {
count += getWrappedChannel().write(src);
}
return count;
}
According to java doc.
Some types of channels, depending upon their state, may write only
some of the bytes or possibly none at all. A socket channel in
non-blocking mode, for example, cannot write any more bytes than are
free in the socket's output buffer.
What configuration is required to fix the following problem?
Akka Actor on 64 bit jvm(machine1) CANNOT use TypedActor proxies on the 32 bit jvm(machine2) (CASE1)
but WORKS vice versa (CASE2).
Is there some configuration setting for serialization I'm missing out?
I'm using akka-2.2.1 from java.
I've a small test code which replicates this problem always.
There are no logs which report "ERRORS", despite enabling even remote-lifecycle events.
It just times out when calling registerListener() on CASE1.
I'm clueless, any help/clue is greatly appreciated.
server.java
public class Server implements ServerActor {
public static final String serverActorName = "server";
public static final String serverIP = "192.168.11.112";
public static final int serverPort = 9999;
public static void main(String[] args) {
new Server();
}
ActorSystem serverSystem;
public Server() {
String network = String
.format("akka.actor.provider = \"akka.remote.RemoteActorRefProvider\" \n"
+ "akka.remote.enabled-transports = [\"akka.remote.netty.tcp\"] \n"
+ "akka.remote.netty.tcp.hostname = \"%s\" \n"
+ "akka.remote.netty.tcp.port = %d", Server.serverIP,
Server.serverPort);
Config config = ConfigFactory.parseString("akka.loglevel = DEBUG \n"
+ "akka.actor.debug.lifecycle = on \n" + network);
serverSystem = ActorSystem.create("sys", config);
RemoteActorRefProvider ref = (RemoteActorRefProvider) serverSystem
.provider();
Address addr = ref.transport().defaultAddress();
String port = addr.port().get().toString();
System.out.printf("Server Akka IP=%s PORT=%s\n", addr, port);
final Server server = this;
// start server service
#SuppressWarnings("unused")
ServerActor proxy = TypedActor.get(serverSystem).typedActorOf(
new TypedProps<Server>(ServerActor.class,
new Creator<Server>() {
private static final long serialVersionUID = 6301999771454618282L;
#Override
public Server create() {
return server;
}
}), Server.serverActorName);
}
#Override
public boolean registerListener(ITestListener listener) {
listener.update(10);
return true;
}
}
And client.java
public class Client implements ITestListener {
public static final String clientActorName = "client";
public static final String clientIP = "192.168.11.111";
public static void main(String[] args) {
new Client();
}
ActorSystem clientSystem;
private ITestListener clientListener = null;
public Client() {
String network = String
.format("akka.actor.provider = \"akka.remote.RemoteActorRefProvider\" \n"
+ "akka.remote.enabled-transports = [\"akka.remote.netty.tcp\"] \n"
+ "akka.remote.netty.tcp.hostname = \"%s\" \n"
+ "akka.remote.netty.tcp.port = 0", Client.clientIP);
Config config = ConfigFactory.parseString("akka.loglevel = DEBUG \n"
+ "akka.actor.debug.lifecycle = on \n" + network);
clientSystem = ActorSystem.create("sys", config);
RemoteActorRefProvider ref = (RemoteActorRefProvider) clientSystem
.provider();
Address addr = ref.transport().defaultAddress();
String port = addr.port().get().toString();
System.out.printf("Client Akka IP=%s PORT=%s\n", addr, port);
final Client client = this;
// start server service
clientListener = TypedActor.get(clientSystem).typedActorOf(
new TypedProps<Client>(ITestListener.class,
new Creator<Client>() {
private static final long serialVersionUID = 2034444366744329184L;
#Override
public Client create() {
return client;
}
}), Client.clientActorName);
connect();
}
private void connect() {
// Connect to remote actor system
String remotePath = String.format("akka.tcp://sys#%s:%d/user/%s",
Server.serverIP, Server.serverPort, Server.serverActorName);
// get remote server proxy object
// TypedActor.context().setReceiveTimeout(Duration.create("3 second"));
ActorRef remoteRef = clientSystem.actorFor(remotePath);
if (remoteRef == null)
throw new RuntimeException("Cannot get remote akka actor");
final ServerActor server = TypedActor.get(clientSystem).typedActorOf(
new TypedProps<ServerActor>(ServerActor.class), remoteRef);
server.registerListener(clientListener);
}
#Override
public void update(int a) {
System.out.printf("*********** Server Sent %d ************\n", a);
}
}