org.metastatic.crypto
Class JKS

java.lang.Object
  |
  +--java.security.KeyStoreSpi
        |
        +--org.metastatic.crypto.JKS

public class JKS
extends java.security.KeyStoreSpi

This is an implementation of Sun's proprietary key store algorithm, called "JKS" for "Java Key Store". This implementation was created entirely through reverse-engineering.

The format of JKS files is, from the start of the file:

  1. Magic bytes. This is a four-byte integer, in big-endian byte order, equal to 0xFEEDFEED.
  2. The version number (probably), as a four-byte integer (all multibyte integral types are in big-endian byte order). The current version number (in modern distributions of the JDK) is 2.
  3. The number of entrires in this keystore, as a four-byte integer. Call this value n
  4. Then, n times:
    1. The entry type, a four-byte int. The value 1 denotes a private key entry, and 2 denotes a trusted certificate.
    2. The entry's alias, formatted as strings such as those written by DataOutput.writeUTF(String).
    3. An eight-byte integer, representing the entry's creation date, in milliseconds since the epoch.

      Then, if the entry is a private key entry:

      1. The size of the encoded key as a four-byte int, then that number of bytes. The encoded key is the DER encoded bytes of the EncryptedPrivateKeyInfo structure (the encryption algorithm is discussed later).
      2. A four-byte integer, followed by that many encoded certificates, encoded as described in the trusted certificates section.

      Otherwise, the entry is a trusted certificate, which is encoded as the name of the encoding algorithm (e.g. X.509), encoded the same way as alias names. Then, a four-byte integer representing the size of the encoded certificate, then that many bytes representing the encoded certificate (e.g. the DER bytes in the case of X.509).

  5. Then, the signature.

(See this file for some idea of how I was able to figure out these algorithms)

Decrypting the key works as follows:

  1. The key length is the length of the ciphertext minus 40. The encrypted key, ekey, is the middle bytes of the ciphertext.
  2. Take the first 20 bytes of the encrypted key as a seed value, K[0].
  3. Compute K[1] ... K[n], where |K[i]| = 20, n = ceil(|ekey| / 20), and K[i] = SHA-1(UTF-16BE(password) + K[i-1]).
  4. key = ekey ^ (K[1] + ... + K[n]).
  5. The last 20 bytes are the checksum, computed as H = SHA-1(UTF-16BE(password) + key). If this value does not match the last 20 bytes of the ciphertext, output FAIL. Otherwise, output key.

The signature is defined as SHA-1(UTF-16BE(password) + US_ASCII("Mighty Aphrodite") + encoded_keystore) (yup, Sun engineers are just that clever).

(Above, SHA-1 denotes the secure hash algorithm, UTF-16BE the big-endian byte representation of a UTF-16 string, and US_ASCII the ASCII byte representation of the string.)

The source code of this class should be available in the file JKS.java.


Constructor Summary
JKS()
           
 
Method Summary
 java.util.Enumeration engineAliases()
           
 boolean engineContainsAlias(java.lang.String alias)
           
 void engineDeleteEntry(java.lang.String alias)
           
 java.security.cert.Certificate engineGetCertificate(java.lang.String alias)
           
 java.lang.String engineGetCertificateAlias(java.security.cert.Certificate cert)
           
 java.security.cert.Certificate[] engineGetCertificateChain(java.lang.String alias)
           
 java.util.Date engineGetCreationDate(java.lang.String alias)
           
 java.security.Key engineGetKey(java.lang.String alias, char[] password)
           
 boolean engineIsCertificateEntry(java.lang.String alias)
           
 boolean engineIsKeyEntry(java.lang.String alias)
           
 void engineLoad(java.io.InputStream in, char[] passwd)
           
 void engineSetCertificateEntry(java.lang.String alias, java.security.cert.Certificate cert)
           
 void engineSetKeyEntry(java.lang.String alias, byte[] encodedKey, java.security.cert.Certificate[] certChain)
           
 void engineSetKeyEntry(java.lang.String alias, java.security.Key key, char[] passwd, java.security.cert.Certificate[] certChain)
           
 int engineSize()
           
 void engineStore(java.io.OutputStream out, char[] passwd)
           
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Constructor Detail

JKS

public JKS()
Method Detail

engineGetKey

public java.security.Key engineGetKey(java.lang.String alias,
                                      char[] password)
                               throws java.security.NoSuchAlgorithmException,
                                      java.security.UnrecoverableKeyException
Specified by:
engineGetKey in class java.security.KeyStoreSpi
java.security.NoSuchAlgorithmException
java.security.UnrecoverableKeyException

engineGetCertificateChain

public java.security.cert.Certificate[] engineGetCertificateChain(java.lang.String alias)
Specified by:
engineGetCertificateChain in class java.security.KeyStoreSpi

engineGetCertificate

public java.security.cert.Certificate engineGetCertificate(java.lang.String alias)
Specified by:
engineGetCertificate in class java.security.KeyStoreSpi

engineGetCreationDate

public java.util.Date engineGetCreationDate(java.lang.String alias)
Specified by:
engineGetCreationDate in class java.security.KeyStoreSpi

engineSetKeyEntry

public void engineSetKeyEntry(java.lang.String alias,
                              java.security.Key key,
                              char[] passwd,
                              java.security.cert.Certificate[] certChain)
                       throws java.security.KeyStoreException
Specified by:
engineSetKeyEntry in class java.security.KeyStoreSpi
java.security.KeyStoreException

engineSetKeyEntry

public void engineSetKeyEntry(java.lang.String alias,
                              byte[] encodedKey,
                              java.security.cert.Certificate[] certChain)
                       throws java.security.KeyStoreException
Specified by:
engineSetKeyEntry in class java.security.KeyStoreSpi
java.security.KeyStoreException

engineSetCertificateEntry

public void engineSetCertificateEntry(java.lang.String alias,
                                      java.security.cert.Certificate cert)
                               throws java.security.KeyStoreException
Specified by:
engineSetCertificateEntry in class java.security.KeyStoreSpi
java.security.KeyStoreException

engineDeleteEntry

public void engineDeleteEntry(java.lang.String alias)
                       throws java.security.KeyStoreException
Specified by:
engineDeleteEntry in class java.security.KeyStoreSpi
java.security.KeyStoreException

engineAliases

public java.util.Enumeration engineAliases()
Specified by:
engineAliases in class java.security.KeyStoreSpi

engineContainsAlias

public boolean engineContainsAlias(java.lang.String alias)
Specified by:
engineContainsAlias in class java.security.KeyStoreSpi

engineSize

public int engineSize()
Specified by:
engineSize in class java.security.KeyStoreSpi

engineIsKeyEntry

public boolean engineIsKeyEntry(java.lang.String alias)
Specified by:
engineIsKeyEntry in class java.security.KeyStoreSpi

engineIsCertificateEntry

public boolean engineIsCertificateEntry(java.lang.String alias)
Specified by:
engineIsCertificateEntry in class java.security.KeyStoreSpi

engineGetCertificateAlias

public java.lang.String engineGetCertificateAlias(java.security.cert.Certificate cert)
Specified by:
engineGetCertificateAlias in class java.security.KeyStoreSpi

engineStore

public void engineStore(java.io.OutputStream out,
                        char[] passwd)
                 throws java.io.IOException,
                        java.security.NoSuchAlgorithmException,
                        java.security.cert.CertificateException
Specified by:
engineStore in class java.security.KeyStoreSpi
java.io.IOException
java.security.NoSuchAlgorithmException
java.security.cert.CertificateException

engineLoad

public void engineLoad(java.io.InputStream in,
                       char[] passwd)
                throws java.io.IOException,
                       java.security.NoSuchAlgorithmException,
                       java.security.cert.CertificateException
Specified by:
engineLoad in class java.security.KeyStoreSpi
java.io.IOException
java.security.NoSuchAlgorithmException
java.security.cert.CertificateException