CryptoUtil.kt (13531B)
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 interface NBO { 56 fun nbo(): ByteArray 57 58 fun signNbo(key: ByteArray): EddsaSignature = CryptoUtil.eddsaSign(this.nbo(), key) 59 60 fun verifyNbo(signature: EddsaSignature, key: EddsaPublicKey): Boolean = CryptoUtil.checkEdssaSignature(this.nbo(), signature, key) 61 } 62 63 /** Helpers for dealing with cryptographic operations in EBICS / LibEuFin */ 64 object CryptoUtil { 65 66 private val provider = BouncyCastleProvider() 67 68 /** Load an RSA private key from its binary PKCS#8 encoding */ 69 fun loadRSAPrivate(encodedPrivateKey: ByteArray): RSAPrivateCrtKey { 70 val spec = PKCS8EncodedKeySpec(encodedPrivateKey) 71 val priv = KeyFactory.getInstance("RSA").generatePrivate(spec) 72 return priv as RSAPrivateCrtKey 73 } 74 75 /** Load an RSA public key from its binary X509 encoding */ 76 fun loadRSAPublic(encodedPublicKey: ByteArray): RSAPublicKey { 77 val spec = X509EncodedKeySpec(encodedPublicKey) 78 val pub = KeyFactory.getInstance("RSA").generatePublic(spec) 79 return pub as RSAPublicKey 80 } 81 82 /** Create an RSA public key from its components: [modulus] and [exponent] */ 83 fun RSAPublicFromComponents(modulus: ByteArray, exponent: ByteArray): RSAPublicKey { 84 val modulusBigInt = BigInteger(1, modulus) 85 val exponentBigInt = BigInteger(1, exponent) 86 val spec = RSAPublicKeySpec(modulusBigInt, exponentBigInt) 87 return KeyFactory.getInstance("RSA").generatePublic(spec) as RSAPublicKey 88 } 89 90 /** Extract an RSA public key from a [raw] X.509 certificate */ 91 fun RSAPublicFromCertificate(raw: ByteArray): RSAPublicKey { 92 val certificate = CertificateFactory.getInstance("X.509").generateCertificate(raw.inputStream()) 93 return certificate.publicKey as RSAPublicKey 94 } 95 96 /** Generate an RSA public key from a [private] one */ 97 fun RSAPublicFromPrivate(private: RSAPrivateCrtKey): RSAPublicKey { 98 val spec = RSAPublicKeySpec(private.modulus, private.publicExponent) 99 return KeyFactory.getInstance("RSA").generatePublic(spec) as RSAPublicKey 100 } 101 102 /** Generate a self-signed X.509 certificate from an RSA [private] key */ 103 fun X509CertificateFromRSAPrivate(private: RSAPrivateCrtKey, name: String): X509Certificate { 104 val start = Date() 105 val calendar = Calendar.getInstance() 106 calendar.time = start 107 calendar.add(Calendar.YEAR, 1_000) 108 val end = calendar.time 109 110 val name = X500Name("CN=$name") 111 val builder = JcaX509v3CertificateBuilder( 112 name, 113 BigInteger(20, Random()), 114 start, 115 end, 116 name, 117 RSAPublicFromPrivate(private) 118 ) 119 120 121 builder.addExtension( 122 Extension.keyUsage, true, KeyUsage( 123 KeyUsage.digitalSignature 124 or KeyUsage.nonRepudiation 125 or KeyUsage.keyEncipherment 126 or KeyUsage.dataEncipherment 127 or KeyUsage.keyAgreement 128 or KeyUsage.keyCertSign 129 or KeyUsage.cRLSign 130 or KeyUsage.encipherOnly 131 or KeyUsage.decipherOnly 132 ) 133 ) 134 builder.addExtension(Extension.basicConstraints, true, BasicConstraints(true)) 135 136 val certificate = JcaContentSignerBuilder("SHA256WithRSA").build(private) 137 return JcaX509CertificateConverter() 138 .setProvider(provider) 139 .getCertificate(builder.build(certificate)) 140 141 } 142 143 /** Generate an RSA key pair of [keysize] */ 144 fun genRSAPair(keysize: Int): Pair<RSAPrivateCrtKey, RSAPublicKey> { 145 val gen = KeyPairGenerator.getInstance("RSA") 146 gen.initialize(keysize) 147 val pair = gen.genKeyPair() 148 return Pair(pair.private as RSAPrivateCrtKey, pair.public as RSAPublicKey) 149 } 150 151 /** Generate an RSA private key of [keysize] */ 152 fun genRSAPrivate(keysize: Int): RSAPrivateCrtKey = genRSAPair(keysize).first 153 154 /** Generate an RSA public key of [keysize] */ 155 fun genRSAPublic(keysize: Int): RSAPublicKey = genRSAPair(keysize).second 156 157 /** 158 * Hash an RSA public key according to the EBICS standard (EBICS 2.5: 4.4.1.2.3). 159 */ 160 fun getEbicsPublicKeyHash(publicKey: RSAPublicKey): ByteArray { 161 val keyBytes = ByteArrayOutputStream() 162 keyBytes.writeBytes(publicKey.publicExponent.encodeHex().trimStart('0').toByteArray()) 163 keyBytes.write(' '.code) 164 keyBytes.writeBytes(publicKey.modulus.encodeHex().trimStart('0').toByteArray()) 165 val digest = MessageDigest.getInstance("SHA-256") 166 return digest.digest(keyBytes.toByteArray()) 167 } 168 169 fun genEbicsE002Key(encryptionPublicKey: RSAPublicKey): Pair<SecretKey, ByteArray> { 170 // Gen transaction key 171 val keygen = KeyGenerator.getInstance("AES", provider) 172 keygen.init(128) 173 val transactionKey = keygen.generateKey() 174 // Encrypt transaction keyA 175 val cipher = Cipher.getInstance( 176 "RSA/None/PKCS1Padding", 177 provider 178 ) 179 cipher.init(Cipher.ENCRYPT_MODE, encryptionPublicKey) 180 val encryptedTransactionKey = cipher.doFinal(transactionKey.encoded) 181 return Pair(transactionKey, encryptedTransactionKey) 182 } 183 184 /** 185 * Encrypt data according to the EBICS E002 encryption process. 186 */ 187 fun encryptEbicsE002( 188 transactionKey: SecretKey, 189 data: InputStream 190 ): CipherInputStream { 191 val cipher = Cipher.getInstance( 192 "AES/CBC/X9.23Padding", 193 provider 194 ) 195 val ivParameterSpec = IvParameterSpec(ByteArray(16)) 196 cipher.init(Cipher.ENCRYPT_MODE, transactionKey, ivParameterSpec) 197 return CipherInputStream(data, cipher) 198 } 199 200 fun decryptEbicsE002Key( 201 privateKey: RSAPrivateCrtKey, 202 encryptedTransactionKey: ByteArray 203 ): SecretKeySpec { 204 val cipher = Cipher.getInstance( 205 "RSA/None/PKCS1Padding", 206 provider 207 ) 208 cipher.init(Cipher.DECRYPT_MODE, privateKey) 209 val transactionKeyBytes = cipher.doFinal(encryptedTransactionKey) 210 return SecretKeySpec(transactionKeyBytes, "AES") 211 } 212 213 fun decryptEbicsE002( 214 transactionKey: SecretKeySpec, 215 encryptedData: InputStream 216 ): CipherInputStream { 217 val cipher = Cipher.getInstance( 218 "AES/CBC/X9.23Padding", 219 provider 220 ) 221 val ivParameterSpec = IvParameterSpec(ByteArray(16)) 222 cipher.init(Cipher.DECRYPT_MODE, transactionKey, ivParameterSpec) 223 return CipherInputStream(encryptedData, cipher) 224 } 225 226 /** 227 * Signing algorithm corresponding to the EBICS A006 signing process. 228 * 229 * Note that while [data] can be arbitrary-length data, in EBICS, the order 230 * data is *always* hashed *before* passing it to the signing algorithm, which again 231 * uses a hash internally. 232 */ 233 fun signEbicsA006(data: ByteArray, privateKey: RSAPrivateCrtKey): ByteArray { 234 val signature = Signature.getInstance("SHA256withRSA/PSS", provider) 235 signature.setParameter(PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1)) 236 signature.initSign(privateKey) 237 signature.update(data) 238 return signature.sign() 239 } 240 241 fun verifyEbicsA006(sig: ByteArray, data: ByteArray, publicKey: RSAPublicKey): Boolean { 242 val signature = Signature.getInstance("SHA256withRSA/PSS", provider) 243 signature.setParameter(PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1)) 244 signature.initVerify(publicKey) 245 signature.update(data) 246 return signature.verify(sig) 247 } 248 249 fun digestEbicsOrderA006(orderData: ByteArray): ByteArray { 250 val digest = MessageDigest.getInstance("SHA-256") 251 for (b in orderData) { 252 when (b) { 253 '\r'.code.toByte(), '\n'.code.toByte(), (26).toByte() -> Unit 254 else -> digest.update(b) 255 } 256 } 257 return digest.digest() 258 } 259 260 fun decryptKey(data: EncryptedPrivateKeyInfo, passphrase: String): RSAPrivateCrtKey { 261 /* make key out of passphrase */ 262 val pbeKeySpec = PBEKeySpec(passphrase.toCharArray()) 263 val keyFactory = SecretKeyFactory.getInstance(data.algName) 264 val secretKey = keyFactory.generateSecret(pbeKeySpec) 265 /* Make a cipher */ 266 val cipher = Cipher.getInstance(data.algName) 267 cipher.init( 268 Cipher.DECRYPT_MODE, 269 secretKey, 270 data.algParameters // has hash count and salt 271 ) 272 /* Ready to decrypt */ 273 val decryptedKeySpec: PKCS8EncodedKeySpec = data.getKeySpec(cipher) 274 val priv = KeyFactory.getInstance("RSA").generatePrivate(decryptedKeySpec) 275 return priv as RSAPrivateCrtKey 276 } 277 278 fun hashStringSHA256(input: String): ByteArray = 279 MessageDigest.getInstance("SHA-256").digest(input.toByteArray(Charsets.UTF_8)) 280 281 fun hashStringNbo(input: String): ByteArray = 282 MessageDigest.getInstance("SHA-512").digest(input.toByteArray(Charsets.UTF_8) + byteArrayOf(0)).sliceArray(0..31) 283 284 fun bcrypt(password: String, salt: ByteArray, cost: Int): ByteArray { 285 val pwBytes = BCrypt.passwordToByteArray(password.toCharArray()) 286 return BCrypt.generate(pwBytes, salt, cost) 287 } 288 289 fun hashArgon2id(password: String, salt: ByteArray): ByteArray { 290 // OSWAP recommended config https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id 291 val builder = Argon2Parameters.Builder(Argon2Parameters.ARGON2_id) 292 .withIterations(1) 293 .withMemoryAsKB(47104) 294 .withParallelism(1) 295 .withSalt(salt) 296 297 val gen = Argon2BytesGenerator() 298 gen.init(builder.build()) 299 300 val result = ByteArray(32) 301 gen.generateBytes(password.toCharArray(), result, 0, result.size) 302 return result 303 } 304 305 private fun mfaBodyHash(body: ByteArray, salt: Base32Crockford16B): Base32Crockford64B { 306 val digest = MessageDigest.getInstance("SHA-512") 307 digest.update(salt.raw) 308 val hash = digest.digest(body) 309 return Base32Crockford64B(hash) 310 } 311 312 fun mfaBodyHashCheck(body: ByteArray, hash: Base32Crockford64B, salt: Base32Crockford16B): Boolean { 313 val check = mfaBodyHash(body, salt) 314 return check == hash 315 } 316 317 fun mfaBodyHashCreate(body: ByteArray): Pair<Base32Crockford64B, Base32Crockford16B> { 318 val salt = Base32Crockford16B.secureRand() 319 val hash = mfaBodyHash(body, salt) 320 return Pair(hash, salt) 321 } 322 323 fun checkEdssaSignature(data: ByteArray, signature: EddsaSignature, publicKey: EddsaPublicKey): Boolean { 324 val pubKey = Ed25519PublicKeyParameters(publicKey.raw, 0) 325 326 val verifier = Ed25519Signer() 327 verifier.init(false, pubKey) 328 verifier.update(data, 0, data.size) 329 330 return verifier.verifySignature(signature.raw) 331 } 332 333 fun eddsaSign(data: ByteArray, privateKey: ByteArray): EddsaSignature { 334 val privateKey = Ed25519PrivateKeyParameters(privateKey, 0) 335 336 val signer = Ed25519Signer() 337 signer.init(true, privateKey) 338 signer.update(data, 0, data.size) 339 340 return EddsaSignature(signer.generateSignature()) 341 } 342 }