CryptoUtil.kt (13018B)
1 /* 2 * This file is part of LibEuFin. 3 * Copyright (C) 2024, 2026 Taler Systems S.A. 4 5 * LibEuFin is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU Affero General Public License as 7 * published by the Free Software Foundation; either version 3, or 8 * (at your option) any later version. 9 10 * LibEuFin is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General 13 * Public License for more details. 14 15 * You should have received a copy of the GNU Affero General Public 16 * License along with LibEuFin; see the file COPYING. If not, see 17 * <http://www.gnu.org/licenses/> 18 */ 19 20 package tech.libeufin.common.crypto 21 22 import org.bouncycastle.asn1.x500.X500Name 23 import org.bouncycastle.asn1.x509.BasicConstraints 24 import org.bouncycastle.asn1.x509.Extension 25 import org.bouncycastle.asn1.x509.KeyUsage 26 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter 27 import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder 28 import org.bouncycastle.crypto.generators.Argon2BytesGenerator 29 import org.bouncycastle.crypto.generators.BCrypt 30 import org.bouncycastle.crypto.params.Argon2Parameters 31 import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters 32 import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters 33 import org.bouncycastle.crypto.signers.Ed25519Signer 34 import org.bouncycastle.jce.provider.BouncyCastleProvider 35 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder 36 import tech.libeufin.common.* 37 import java.io.ByteArrayOutputStream 38 import java.io.InputStream 39 import java.math.BigInteger 40 import java.security.KeyFactory 41 import java.security.KeyPairGenerator 42 import java.security.MessageDigest 43 import java.security.Signature 44 import java.security.cert.CertificateFactory 45 import java.security.cert.X509Certificate 46 import java.security.interfaces.RSAPrivateCrtKey 47 import java.security.interfaces.RSAPublicKey 48 import java.security.spec.* 49 import java.util.* 50 import javax.crypto.* 51 import javax.crypto.spec.IvParameterSpec 52 import javax.crypto.spec.PBEKeySpec 53 import javax.crypto.spec.SecretKeySpec 54 55 /** Helpers for dealing with cryptographic operations in EBICS / LibEuFin */ 56 object CryptoUtil { 57 58 private val provider = BouncyCastleProvider() 59 60 /** Load an RSA private key from its binary PKCS#8 encoding */ 61 fun loadRSAPrivate(encodedPrivateKey: ByteArray): RSAPrivateCrtKey { 62 val spec = PKCS8EncodedKeySpec(encodedPrivateKey) 63 val priv = KeyFactory.getInstance("RSA").generatePrivate(spec) 64 return priv as RSAPrivateCrtKey 65 } 66 67 /** Load an RSA public key from its binary X509 encoding */ 68 fun loadRSAPublic(encodedPublicKey: ByteArray): RSAPublicKey { 69 val spec = X509EncodedKeySpec(encodedPublicKey) 70 val pub = KeyFactory.getInstance("RSA").generatePublic(spec) 71 return pub as RSAPublicKey 72 } 73 74 /** Create an RSA public key from its components: [modulus] and [exponent] */ 75 fun RSAPublicFromComponents(modulus: ByteArray, exponent: ByteArray): RSAPublicKey { 76 val modulusBigInt = BigInteger(1, modulus) 77 val exponentBigInt = BigInteger(1, exponent) 78 val spec = RSAPublicKeySpec(modulusBigInt, exponentBigInt) 79 return KeyFactory.getInstance("RSA").generatePublic(spec) as RSAPublicKey 80 } 81 82 /** Extract an RSA public key from a [raw] X.509 certificate */ 83 fun RSAPublicFromCertificate(raw: ByteArray): RSAPublicKey { 84 val certificate = CertificateFactory.getInstance("X.509").generateCertificate(raw.inputStream()) 85 return certificate.publicKey as RSAPublicKey 86 } 87 88 /** Generate an RSA public key from a [private] one */ 89 fun RSAPublicFromPrivate(private: RSAPrivateCrtKey): RSAPublicKey { 90 val spec = RSAPublicKeySpec(private.modulus, private.publicExponent) 91 return KeyFactory.getInstance("RSA").generatePublic(spec) as RSAPublicKey 92 } 93 94 /** Generate a self-signed X.509 certificate from an RSA [private] key */ 95 fun X509CertificateFromRSAPrivate(private: RSAPrivateCrtKey, name: String): X509Certificate { 96 val start = Date() 97 val calendar = Calendar.getInstance() 98 calendar.time = start 99 calendar.add(Calendar.YEAR, 1_000) 100 val end = calendar.time 101 102 val name = X500Name("CN=$name") 103 val builder = JcaX509v3CertificateBuilder( 104 name, 105 BigInteger(20, Random()), 106 start, 107 end, 108 name, 109 RSAPublicFromPrivate(private) 110 ) 111 112 113 builder.addExtension(Extension.keyUsage, true, KeyUsage( 114 KeyUsage.digitalSignature 115 or KeyUsage.nonRepudiation 116 or KeyUsage.keyEncipherment 117 or KeyUsage.dataEncipherment 118 or KeyUsage.keyAgreement 119 or KeyUsage.keyCertSign 120 or KeyUsage.cRLSign 121 or KeyUsage.encipherOnly 122 or KeyUsage.decipherOnly 123 )) 124 builder.addExtension(Extension.basicConstraints, true, BasicConstraints(true)) 125 126 val certificate = JcaContentSignerBuilder("SHA256WithRSA").build(private) 127 return JcaX509CertificateConverter() 128 .setProvider(provider) 129 .getCertificate(builder.build(certificate)) 130 131 } 132 133 /** Generate an RSA key pair of [keysize] */ 134 fun genRSAPair(keysize: Int): Pair<RSAPrivateCrtKey, RSAPublicKey> { 135 val gen = KeyPairGenerator.getInstance("RSA") 136 gen.initialize(keysize) 137 val pair = gen.genKeyPair() 138 return Pair(pair.private as RSAPrivateCrtKey, pair.public as RSAPublicKey) 139 } 140 /** Generate an RSA private key of [keysize] */ 141 fun genRSAPrivate(keysize: Int): RSAPrivateCrtKey = genRSAPair(keysize).first 142 /** Generate an RSA public key of [keysize] */ 143 fun genRSAPublic(keysize: Int): RSAPublicKey = genRSAPair(keysize).second 144 145 /** 146 * Hash an RSA public key according to the EBICS standard (EBICS 2.5: 4.4.1.2.3). 147 */ 148 fun getEbicsPublicKeyHash(publicKey: RSAPublicKey): ByteArray { 149 val keyBytes = ByteArrayOutputStream() 150 keyBytes.writeBytes(publicKey.publicExponent.encodeHex().trimStart('0').toByteArray()) 151 keyBytes.write(' '.code) 152 keyBytes.writeBytes(publicKey.modulus.encodeHex().trimStart('0').toByteArray()) 153 val digest = MessageDigest.getInstance("SHA-256") 154 return digest.digest(keyBytes.toByteArray()) 155 } 156 157 fun genEbicsE002Key(encryptionPublicKey: RSAPublicKey): Pair<SecretKey, ByteArray> { 158 // Gen transaction key 159 val keygen = KeyGenerator.getInstance("AES", provider) 160 keygen.init(128) 161 val transactionKey = keygen.generateKey() 162 // Encrypt transaction keyA 163 val cipher = Cipher.getInstance( 164 "RSA/None/PKCS1Padding", 165 provider 166 ) 167 cipher.init(Cipher.ENCRYPT_MODE, encryptionPublicKey) 168 val encryptedTransactionKey = cipher.doFinal(transactionKey.encoded) 169 return Pair(transactionKey, encryptedTransactionKey) 170 } 171 172 /** 173 * Encrypt data according to the EBICS E002 encryption process. 174 */ 175 fun encryptEbicsE002( 176 transactionKey: SecretKey, 177 data: InputStream 178 ): CipherInputStream { 179 val cipher = Cipher.getInstance( 180 "AES/CBC/X9.23Padding", 181 provider 182 ) 183 val ivParameterSpec = IvParameterSpec(ByteArray(16)) 184 cipher.init(Cipher.ENCRYPT_MODE, transactionKey, ivParameterSpec) 185 return CipherInputStream(data, cipher) 186 } 187 188 fun decryptEbicsE002Key( 189 privateKey: RSAPrivateCrtKey, 190 encryptedTransactionKey: ByteArray 191 ): SecretKeySpec { 192 val cipher = Cipher.getInstance( 193 "RSA/None/PKCS1Padding", 194 provider 195 ) 196 cipher.init(Cipher.DECRYPT_MODE, privateKey) 197 val transactionKeyBytes = cipher.doFinal(encryptedTransactionKey) 198 return SecretKeySpec(transactionKeyBytes, "AES") 199 } 200 201 fun decryptEbicsE002( 202 transactionKey: SecretKeySpec, 203 encryptedData: InputStream 204 ): CipherInputStream { 205 val cipher = Cipher.getInstance( 206 "AES/CBC/X9.23Padding", 207 provider 208 ) 209 val ivParameterSpec = IvParameterSpec(ByteArray(16)) 210 cipher.init(Cipher.DECRYPT_MODE, transactionKey, ivParameterSpec) 211 return CipherInputStream(encryptedData, cipher) 212 } 213 214 /** 215 * Signing algorithm corresponding to the EBICS A006 signing process. 216 * 217 * Note that while [data] can be arbitrary-length data, in EBICS, the order 218 * data is *always* hashed *before* passing it to the signing algorithm, which again 219 * uses a hash internally. 220 */ 221 fun signEbicsA006(data: ByteArray, privateKey: RSAPrivateCrtKey): ByteArray { 222 val signature = Signature.getInstance("SHA256withRSA/PSS", provider) 223 signature.setParameter(PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1)) 224 signature.initSign(privateKey) 225 signature.update(data) 226 return signature.sign() 227 } 228 229 fun verifyEbicsA006(sig: ByteArray, data: ByteArray, publicKey: RSAPublicKey): Boolean { 230 val signature = Signature.getInstance("SHA256withRSA/PSS", provider) 231 signature.setParameter(PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1)) 232 signature.initVerify(publicKey) 233 signature.update(data) 234 return signature.verify(sig) 235 } 236 237 fun digestEbicsOrderA006(orderData: ByteArray): ByteArray { 238 val digest = MessageDigest.getInstance("SHA-256") 239 for (b in orderData) { 240 when (b) { 241 '\r'.code.toByte(), '\n'.code.toByte(), (26).toByte() -> Unit 242 else -> digest.update(b) 243 } 244 } 245 return digest.digest() 246 } 247 248 fun decryptKey(data: EncryptedPrivateKeyInfo, passphrase: String): RSAPrivateCrtKey { 249 /* make key out of passphrase */ 250 val pbeKeySpec = PBEKeySpec(passphrase.toCharArray()) 251 val keyFactory = SecretKeyFactory.getInstance(data.algName) 252 val secretKey = keyFactory.generateSecret(pbeKeySpec) 253 /* Make a cipher */ 254 val cipher = Cipher.getInstance(data.algName) 255 cipher.init( 256 Cipher.DECRYPT_MODE, 257 secretKey, 258 data.algParameters // has hash count and salt 259 ) 260 /* Ready to decrypt */ 261 val decryptedKeySpec: PKCS8EncodedKeySpec = data.getKeySpec(cipher) 262 val priv = KeyFactory.getInstance("RSA").generatePrivate(decryptedKeySpec) 263 return priv as RSAPrivateCrtKey 264 } 265 266 fun hashStringSHA256(input: String): ByteArray = 267 MessageDigest.getInstance("SHA-256").digest(input.toByteArray(Charsets.UTF_8)) 268 269 fun bcrypt(password: String, salt: ByteArray, cost: Int): ByteArray { 270 val pwBytes = BCrypt.passwordToByteArray(password.toCharArray()) 271 return BCrypt.generate(pwBytes, salt, cost) 272 } 273 274 fun hashArgon2id(password: String, salt: ByteArray): ByteArray { 275 // OSWAP recommended config https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id 276 val builder = Argon2Parameters.Builder(Argon2Parameters.ARGON2_id) 277 .withIterations(1) 278 .withMemoryAsKB(47104) 279 .withParallelism(1) 280 .withSalt(salt) 281 282 val gen = Argon2BytesGenerator() 283 gen.init(builder.build()) 284 285 val result = ByteArray(32) 286 gen.generateBytes(password.toCharArray(), result, 0, result.size) 287 return result 288 } 289 290 private fun mfaBodyHash(body: ByteArray, salt: Base32Crockford16B): Base32Crockford64B { 291 val digest = MessageDigest.getInstance("SHA-512") 292 digest.update(salt.raw) 293 val hash = digest.digest(body) 294 return Base32Crockford64B(hash) 295 } 296 297 fun mfaBodyHashCheck(body: ByteArray, hash: Base32Crockford64B, salt: Base32Crockford16B): Boolean { 298 val check = mfaBodyHash(body, salt) 299 return check == hash 300 } 301 302 fun mfaBodyHashCreate(body: ByteArray): Pair<Base32Crockford64B, Base32Crockford16B> { 303 val salt = Base32Crockford16B.secureRand() 304 val hash = mfaBodyHash(body, salt) 305 return Pair(hash, salt) 306 } 307 308 fun checkEdssaSignature(data: ByteArray, signature: EddsaSignature, publicKey: EddsaPublicKey): Boolean { 309 val pubKey = Ed25519PublicKeyParameters(publicKey.raw, 0) 310 311 val verifier = Ed25519Signer() 312 verifier.init(false, pubKey) 313 verifier.update(data, 0, data.size) 314 315 return verifier.verifySignature(signature.raw) 316 } 317 318 fun eddsaSign(data: ByteArray, privateKey: ByteArray): EddsaSignature { 319 val privateKey = Ed25519PrivateKeyParameters(privateKey, 0) 320 321 val signer = Ed25519Signer() 322 signer.init(true, privateKey) 323 signer.update(data, 0, data.size) 324 325 return EddsaSignature(signer.generateSignature()) 326 } 327 }