pkixops-crypto

pkixops-crypto provides cryptographic utilities used by PKIXOps projects. It is designed to be lightweight, easy to embed, and friendly for Java/Spring services.

Features

This module focuses on practical building blocks used in PKI automation services and wallet provider backends.

Installation (Maven)

<dependency>
  <groupId>com.pkixops</groupId>
  <artifactId>pkixops-crypto</artifactId>
  <version>1.2.1</version>
</dependency>

(Adjust the version to your latest release.)

Examples

The following examples are based on a single flow (similar to your MobileIdMain) to demonstrate how each primitive is used.

Base64

String plainText = "This is a Java example.";

String base64Encoding = Base64Util.encode(plainText);
System.out.println("base 64 encoding : " + base64Encoding);

String base64Decoding = Base64Util.decode(base64Encoding);
System.out.println("base 64 decoding : " + base64Decoding);

Quick Start: Base58

byte[] dataBytes = plainText.getBytes(StandardCharsets.UTF_8);
String base58Encoding = BASE58.encode(dataBytes);
System.out.println("base 58 encoding : " + base58Encoding);

byte[] decodedBytes = BASE58.decode(base58Encoding);
String base58Decoding = new String(decodedBytes, StandardCharsets.UTF_8);
System.out.println("base 58 decoding : " + base58Decoding);

HASH (SHA-512)

String nonce = Generator.genNonce();

byte[] hash = HASH.SHA512(nonce);
String hexString = HASH.toHex(hash);

System.out.println("SHA 512 : " + hexString);

AES AES-128 / AES-256

AES support depends on your runtime policy and JCE restrictions. You can check the maximum allowed key length:

int max = Cipher.getMaxAllowedKeyLength("AES");
if (max >= 256) {
  System.out.println("AES 256 Enable");
} else {
  System.out.println("AES 128 Enable");
}

AES with String key/IV

String aesKey = "1234567890ABCDEF1234567890ABCDEF"; // 32 chars = 256-bit
String aesIv  = "1234567890ABCDEF";                 // 16 chars = 128-bit IV

String encText = AES.encrypt(aesKey, aesIv, plainText);
String decText = AES.decrypt(aesKey, aesIv, encText);

AES with byte[] key/IV (recommended for hybrid flows)

// randomData -> SHA512 -> sKey(32 bytes), iV(16 bytes)
byte[] sKey = Arrays.copyOfRange(HASH.SHA512(randomData), 0, 32);
byte[] iV   = Arrays.copyOfRange(HASH.SHA512(randomData), 32, 48);

String encData = AES.encrypt(sKey, iV, plainText);
String decData = AES.decrypt(sKey, iV, encData);
Security note: Use cryptographically secure random values for keys/IVs, and avoid fixed keys/IVs in production. When using AES-CBC, the IV must be unpredictable per message. If you use AES-GCM, prefer unique nonces and authenticated encryption.

ECDSA P-256 / secp256r1

The example below generates a P-256 keypair, exports to PEM, signs using SHA-256 with ECDSA, and verifies the signature.

// 1) Generate EC P-256 keypair (secp256r1 / prime256v1)
KeyPair keyPair = ECDSA.generateP256KeyPair();

// 2) Export to PEM
String pubPem = PemUtil.toPem(keyPair.getPublic());
String priPem = PemUtil.toPem(keyPair.getPrivate());
System.out.println(pubPem);
System.out.println(priPem);

// 3) Sign (SHA-256 + ECDSA)
byte[] data = plainText.getBytes(StandardCharsets.UTF_8);
PrivateKey privateKey = PemUtil.fromSec1EcPrivateKeyPem(priPem, "secp256r1");
byte[] signature = ECDSA.signSha256Ecdsa(privateKey, data);
String sigB64 = Base64.getEncoder().encodeToString(signature);
System.out.println("Signature(Base64): " + sigB64);

// 4) Verify
PublicKey publicKey = PemUtil.fromEcPublicKeyPem(pubPem);
boolean ok = ECDSA.verifySha256Ecdsa(publicKey, data, signature);
System.out.println("Verify result: " + ok);

// 5) Tamper check (expected false)
boolean fail = ECDSA.verifySha256Ecdsa(publicKey, "tampered".getBytes(StandardCharsets.UTF_8), signature);
System.out.println("Verify tampered: " + fail);

RSA 2048

RSA utilities include keypair generation, Base58 transport of keys, and OAEP encryption/decryption.

KeyPair + Base58 transport

int keySize = 2048;

// 1) Generate RSA keypair
KeyPair keyPair = RSA.generateRsaKeyPair(keySize);

// 2) Export public/private to Base58 (compact string transport)
String publicKey58 = PemUtil.publicKeyToBase58(keyPair.getPublic());
String privateKey58 = PemUtil.privateKeyToBase58(keyPair.getPrivate());

System.out.println("PublicKey(Base58)  = " + publicKey58);
System.out.println("PrivateKey(Base58) = " + privateKey58);

// 3) Restore and compare
PublicKey restoredPublicKey = PemUtil.publicKeyFromBase58(publicKey58);
PrivateKey restoredPrivateKey = PemUtil.privateKeyFromBase58(privateKey58);

System.out.println("Restored equals original? " +
  Arrays.equals(keyPair.getPublic().getEncoded(), restoredPublicKey.getEncoded()));

System.out.println("Restored equals original? " +
  Arrays.equals(keyPair.getPrivate().getEncoded(), restoredPrivateKey.getEncoded()));

RSA-OAEP encrypt/decrypt

// Encrypt with Base58 public key
byte[] encrypted = RSA.encryptOaepFromBase58PublicKey(publicKey58, plainText.getBytes(StandardCharsets.UTF_8));
String enc58 = BASE58.encode(encrypted);
System.out.println("encrypted rsa : " + enc58);

// Decrypt with Base58 private key
byte[] cipherText = BASE58.decode(enc58);
byte[] decrypted = RSA.decryptOaepFromBase58PrivateKey(privateKey58, cipherText);
System.out.println("decrypted rsa : " + new String(decrypted, StandardCharsets.UTF_8));
Security note: Use OAEP (as shown) instead of legacy RSA PKCS#1 v1.5 encryption when possible. RSA is typically used to encrypt small payloads (e.g., a session key), not large messages.

Hybrid Encryption (RSA + AES)

A common pattern: RSA encrypts a random secret, and AES encrypts the real payload.

Encrypt

// 1) randomData -> RSA (public) -> Base58
String randomData = Generator.genNonce48();
byte[] rsaEncrypted = RSA.encryptOaepFromBase58PublicKey(publicKey58, randomData.getBytes(StandardCharsets.UTF_8));
String enc58 = BASE58.encode(rsaEncrypted);

// 2) randomData -> SHA-512 -> sKey(32 bytes), iV(16 bytes)
byte[] sKey = Arrays.copyOfRange(HASH.SHA512(randomData), 0, 32);
byte[] iV   = Arrays.copyOfRange(HASH.SHA512(randomData), 32, 48);

// 3) AES encrypt payload
String encData = AES.encrypt(sKey, iV, plainText);
System.out.println("[AES] AES 256 Encrypt : " + encData);

Decrypt

// 1) Base58 -> RSA (private) -> randomData
byte[] encrypted = BASE58.decode(enc58);
byte[] decrypted = RSA.decryptOaepFromBase58PrivateKey(privateKey58, encrypted);
String randomData = new String(decrypted, StandardCharsets.UTF_8);

// 2) randomData -> SHA-512 -> sKey, iV
byte[] sKey = Arrays.copyOfRange(HASH.SHA512(randomData), 0, 32);
byte[] iV   = Arrays.copyOfRange(HASH.SHA512(randomData), 32, 48);

// 3) AES decrypt payload
String decText = AES.decrypt(sKey, iV, encData);
System.out.println("[AES] AES 256 Decrypt : " + decText);

Notes on Security

License

The Apache License, Version 2.0