Standard support
In general the JCA implements well specified algorithms. The trick is to use those algorithms both for your Java and for the other languages. You may want to map specific algorithms to the specifications and then take a look at those. Fortunately, Java is relatively well behaved and has a specific document that lists all the algorithms.
For instance the signature scheme "SHA256withRSA" is defined as "The RSA signature algorithm that uses the SHA-* digest with the RSASSA-PKCS1-v1_5 signature scheme as defined in PKCS #1 v2.2." So that way you know that you're dealing with a PKCS#1 v1.5 signature. If you want to maintain compatibility you should make sure that the referenced standards are part of modern, mainstream cryptography.
As long as you can find implementations of the same standard that are binary compatible then you can implement the scheme in any language. Beware though that many languages / runtimes are documented very poorly; many do not even link to the standards that are implemented.
Beware that the algorithm string is just one way to configure the algorithm. The algorithm can often be tweaked using instances of AlgorithmParameterSpec
, for instance GCMParameterSpec can be used to set the tag size for GCM.
Existing protocols
In general, using the cryptography classes does require significant knowledge about cryptography and secure development. As such, the JCA is more of a toolbox for professionals. The countless insecure constructions that get posted to StackOverflow shows what happens if you use a powertool without any instructions.
Quite often it is better to keep to higher level standards such as the CMS (i.e. PKCS#7) or PGP "container formats" or of course transport protocols such as TLS and SSH. Those need to be configured correctly, but that's often easier than protocol design.
Pitfalls
Of course, all that doesn't mean that there aren't any other pitfalls; for instance:
- Most modern cryptography is described as binary, and most of the JCA classes of Java operate on bytes or, more specifically byte arrays (or a wrapper class called ByteBuffer). Some languages automatically perform some kind of encoding / decoding and that may make interop harder to handle. Actually, the highest percentage of cryptographic questions on StackOverflow are related to encoding issues.
"SHA256withECDSA"
is using the "The ECDSA signature algorithms as defined in ANSI X9.62." which delivers an output of ASN.1 / DER encoded values of r and s as binary signature. ANSI X9.62 is a "payware" standard, and other libraries may just output a "flat" signature consisting of a static sized r and s. This is supported in the Oracle provider as "SHA256withECDSAinP1363Format"
.
- Key Derivation Functions are a general issue, and there isn't a specific KDF class in Java 17. Key Derivation Functions are generally also part of key agreement, and it is often hard to create a compatible implementation of key agreement across platforms. These are badly standardized as well.
- Sometimes the standards themselves are not very clear; e.g. the PSS and OAEP protocols do not define a specific hash as standard for the internal MGF1 mask generation function. In other implementations usually it is the same as the hash used for the message, but Oracles provider always uses SHA-1 as default.
- RSA encryption does not actually use ECB mode even if it is specified as
"RSA/ECB/PKCS1Padding"
or similar; RSA doesn't use any block mode of operation (i.e. "ECB"
should be absent or read "None"
).
Recommendations
Some generic recommendations:
- For ciphers I'd stick to AES in CBC + PKCS#5/7 padding or, preferably, authenticated GCM mode.
- Always assume that a random number generator does just that: generate random numbers. Finding a compatible RNG is likely to result in failures. For Java I'd mostly just use
new SecureRandom()
.