This is a support class for an authentications scheme dubbed Secure Basic Authentication. As a protocol, secure basic authentication is identical to basic authentication as described in RFC 7617. The differences are in how passwords are created and compared, and in how realms are named. Generally, a secure-basic-authentication password is a URL-safe, base 64 encoding of a sequence of bytes. The first four bytes is a timestamp of a 32-bit two's complement integer, stored in little-endian byte order, providing the time at which the password was created as the number of seconds since 1970-01-01T00:00:00Z. The next four bytes is a 32-bit CRC of the first 4 bytes of the sequence followed by the password as an array of bytes using the UTF-8 character encoding. The remainder of the sequence is either
- a SHA-256 message digest of (1) the first eight bytes of the sequence and (2) a password using the UTF-8 character encoding.
- a digital signature of (1) the first eight bytes of the sequence and (2) a password using the UTF-8 character encoding.
- a digital signature of (1) the first eight bytes of the sequence, (2) the DER encoding of the public key provided in an SSL certificate, and (3) a password using the UTF-8 character encoding.
- [D]. This corresponds to Case 1 above. The
method
iconedRealm(String)
will replace this sequence with the Unicode character whose codepoint is 0x231A, which looks like a watch. - [S]. This corresponds to Case 2 above. The
method
iconedRealm(String)
will replace this sequence with the Unicode character whose codepoint is 0x1F58A, which looks like a pen. - [SC]. This corresponds to Case 3 above.
The method
iconedRealm(String)
will replace this sequence with the Unicode character whose codepoint is 0x1F85, followed by the Unicode character whose code point is 0x1F512. This combination looks like a pen followed by a lock.
- it is very unlikely for anyone to use an emoji as part of the name of a realm.
- emoji are easily distinguished from text. It is easy to create a "helper" application that can create the password used in secure basic authentication and paste that password into a password field provided by a browser or other program that does not support secure basic authentication, and the emoji will allow a user to determine which type of authentication is expected.
A client can create a PEM-encoded key pair by calling
createPEMPair()
, which returns an array
containing two strings. The first is a PEM encoded private key
and the second is a PEM encoded public key. A user's PEM encoded public
key can be provided to a server along with a password. To create
a password a client will call the constructor
SecureBasicUtilities(String)
, using
its PEM-encoded private key as an argument, and then the method
createPassword(Certificate, char[])
to
create the password to be given to the server. In this case the
first argument is the certificate provided by the server and the
second argument is the user's password.
A server will call SecureBasicUtilities(String)
using the PEM-encoded public key for a client as an argument. It
will decode the password provided by the user by calling
decodePassword(String)
, and then
getTimeDiff(byte[])
to check a timestamp.
Then it will call
checkPassword(byte[],Certificate,String)
with the decoded password as its first argument, the server's certificate
as its second argument, and the stored password for the user as its
third argument.
Finally, there are several 'utility' methods for handling realms, and both a constructor and a method for the case where a key store is used to hold the keys.
NOTE: For compatibility with openssl, one should use the keytool
program, or
createPEMPair(File,String,String,String,String,char[])
,
to generate a key pair as a PKCS #12 file will then be created.
The openssl equivalent to
iskeytool -genkey -keyalg EC -groupname secp256r1 \ -sigalg SHA256withECDSA -dname CN=nobody@nowhere.com \ -alias key -keypass password -storepass password \ -keystore ecstore.pfx
although the choice of a signature algorithm (used to self sign) may be different. To add to the confusion, for the elliptic curve used in this example, keytool prefers the name secp256r1 whereas openssl prefers prime256v1. When openssl is given the name secp256r1, it will indicate that is is using prime256v1, whereas when keytool is given the name prime256v1, it generates an error message. Also keytool must use the same password for the file as for each entry it stores if the file is to be compatible with openssl.openssl ecparam -name prime256v1 -genkey -noout -out eckey.pem openssl req -new -x509 -key eckey.pem -out eccert.pem -days 360 openssl pkcs12 -export -inkey eckey.pem -in eccert.pem \ -name key -out ecstore.pfx
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic enum
Type of the generated password.static enum
Get the type of an instance ofSecureBasicUtilities
. -
Constructor Summary
ConstructorsConstructorDescriptionConstructor for the message-digest case.SecureBasicUtilities
(File file, String sigalg, String alias, char[] pwarray) Constructor given a key store.Constructor given an input stream.Constructor given a string containing PEM-encoded data. -
Method Summary
Modifier and TypeMethodDescriptionboolean
checkPassword
(byte[] sigarray, Certificate cert, String password) Determine if a password is valid.char[]
createPassword
(Certificate cert, char[] password) Create a password based on digital signatures or message digests.static String[]
Generate two PEM encoded strings for a key pair, each with a header specifying a signature algorithm.static String[]
createPEMPair
(File keystoreFile, String pspec, String sigalg, String alias, String dn, char[] pwarray) Generate two PEM encoded strings for a key pair, specifying an elliptic curve and a signature algorithm, storing the keys in a PKCS #12 file.static String[]
createPEMPair
(String pspec, String sigalg) Generate two PEM encoded strings for a key pair, specifying an elliptic curve and a signature algorithm.static byte[]
decodePassword
(byte[] password) Decode an encoded password represented as an array of bytes.static byte[]
decodePassword
(char[] password) Decode an encoded password represented as an array of chars.static byte[]
decodePassword
(String password) Decode an encoded password represented as a string.static String
decodeRealm
(String realm) Get the realm for from a string representing an encoded realm.static String
encodeRealm
(String realm, SecureBasicUtilities.Mode mode) Get the encoded realm for from a string representing a realm.Get the encryption algorithm used for the public and/or private keys for this instance ofSecureBasicUtilities
.static SecureBasicUtilities.Mode
Get the mode for from a string representing an encoded realm.String[]
Get PEM-encoded strings representing public and private keys with the signature algorithm as a header.Get the private key for this instance ofSecureBasicUtilities
.Get the public key for this instance ofSecureBasicUtilities
.Get the signature algorithm used for the public and/or private keys for this instance ofSecureBasicUtilities
.Get an initializedSignature
for signing.static int
getTimeDiff
(byte[] sigarray) Given a decoded password, find the time difference between the current time and the password's timestamp.getType()
Get the type of for this instance ofSecureBasicUtilities
.Get an initializedSignature
for verification.static String
iconedRealm
(String realm)
-
Constructor Details
-
SecureBasicUtilities
public SecureBasicUtilities()Constructor for the message-digest case. -
SecureBasicUtilities
public SecureBasicUtilities(String pem) throws IOException, IllegalArgumentException, GeneralSecurityException Constructor given a string containing PEM-encoded data. The encoded string will start with a header,signature-algorithm
, that provides the name of a signature algorithm used in creating or verifying digital signatures. This header's textual representation consists of a line starting with the string "signature-algorithm:", followed by optional white space and the name of the signature algorithm. The default signature algorithm is named SHA256withECDSA. The header will be followed by PEM encoded data whose type is eitherCERTIFICATE
,EC PUBLIC KEY
, orEC PRIVATE KEY
(while EC is the default and is preferred for performance reasons, some other algorithm such as RSA can be used instead. can be used if desired, in which case 'EC' should be replaced with the name of the algorithm).Note: when this constructor is used to read a private key, the key must be DER encoded as is done with
Key.getEncoded()
: Java makes it difficult to directly read a private key, preferring the use of a key store instead.- Parameters:
pem
- the PEM string.- Throws:
IOException
IllegalArgumentException
GeneralSecurityException
-
SecureBasicUtilities
public SecureBasicUtilities(InputStream is) throws IOException, IllegalArgumentException, GeneralSecurityException Constructor given an input stream. The input stream will start with a header,signature-algorithm
, that provides the name of a signature algorithm used in creating or verifying digital signatures. This header's textual representation consists of a line starting with the string "signature-algorithm:", followed by optional whitespace and the name of the signature algorithm. The header will be followed by PEM encoded data whose type is eitherCERTIFICATE
,EC PUBLIC KEY
, orEC PRIVATE KEY
(while EC is the default and is preferred for performance reasons) some other algorithm such as RSA can be used instead. can be used if desired). The data must be UTF-8 encoded.- Parameters:
is
- the input stream- Throws:
IOException
IllegalArgumentException
GeneralSecurityException
-
SecureBasicUtilities
public SecureBasicUtilities(File file, String sigalg, String alias, char[] pwarray) throws IOException, IllegalArgumentException, GeneralSecurityException Constructor given a key store. The key store should use the PKCS#12 format, but this is not required. The same password has to be used for the keystore itself and for the private key corresponding to the alias.This constructor is particularly useful if the keys are created using a program such as openssl: with the exception of some special cases, Java makes it difficult to read in private keys without using a key store.
- Parameters:
file
- the file containing the key storesigalg
- the signature algorithm (e.g., SHA256withECDSA); null for the defaultalias
- a name used to look up a key or certificatepwarray
- an array of characters providing a password- Throws:
IOException
IllegalArgumentException
GeneralSecurityException
-
-
Method Details
-
getMode
Get the mode for from a string representing an encoded realm. When the return value is notSecureBasicUtilities.Mode.PASSWORD
, an encoded realm will have started with one or two emojis, optionally followed by a space.- Parameters:
realm
- the encoded realm.- Returns:
- the mode
SecureBasicUtilities.Mode.DIGEST
,SecureBasicUtilities.Mode.SIGNATURE_WITHOUT_CERT
,SecureBasicUtilities.Mode.SIGNATURE_WITH_CERT
, orSecureBasicUtilities.Mode.PASSWORD
- See Also:
-
encodeRealm
Get the encoded realm for from a string representing a realm. When the mode value is notSecureBasicUtilities.Mode.PASSWORD
, the encoded realm will have started with one or two emojis, followed by a space:- for
SecureBasicUtilities.Mode.DIGEST
, the encoded string starts with the Unicode character whose code point is 0x231A. This symbol looks like a watch to indicate that the digest uses a time field in addition to a signature. - for
SecureBasicUtilities.Mode.SIGNATURE_WITHOUT_CERT
, the encoded string starts with the Unicode character whose code point is 0x1F58A. This symbol looks like a pen to indicate that a digital signature used. - for
SecureBasicUtilities.Mode.SIGNATURE_WITH_CERT
, the encoded string starts with the Unicode character whose code point is 0x1F58A, followed by the Unicode character whose code point is 0x1F512. The first symbol looks like a pen to indicate that a digital signature used, and the second looks like a lock to indicate that the public key from an SSL/TLS certificate is included in the signature.
- Parameters:
realm
- the realmmode
- the mode determining the type of encoding (SecureBasicUtilities.Mode.DIGEST
,SecureBasicUtilities.Mode.SIGNATURE_WITHOUT_CERT
,SecureBasicUtilities.Mode.SIGNATURE_WITH_CERT
, orSecureBasicUtilities.Mode.PASSWORD
)- Returns:
- the encoded realm
- See Also:
- for
-
decodeRealm
Get the realm for from a string representing an encoded realm. When the value of the mode used when the realm was encoded is notSecureBasicUtilities.Mode.PASSWORD
, the encoded realm will have started with one or two emojis, followed optionally by a space.- Parameters:
realm
- the realm- Returns:
- the encoded realm
- See Also:
-
iconedRealm
-
getType
Get the type of for this instance ofSecureBasicUtilities
. The type determines if whether or not a public key and/or a private key is available. -
getPrivateKey
Get the private key for this instance ofSecureBasicUtilities
.- Returns:
- the private key; null if there is none
-
getPublicKey
Get the public key for this instance ofSecureBasicUtilities
.- Returns:
- the public key; null if there is none
-
getEncryptionAlgorithm
Get the encryption algorithm used for the public and/or private keys for this instance ofSecureBasicUtilities
.- Returns:
- the encryption algorithm
-
getSignatureAlgorithm
Get the signature algorithm used for the public and/or private keys for this instance ofSecureBasicUtilities
.- Returns:
- the signature algorithm
-
getSigner
Get an initializedSignature
for signing. The caller should use theSignature
methods namedupdate
andsign
to create the signature.- Returns:
- an initialized
Signature
; null if there this object was not created with a private key - Throws:
GeneralSecurityException
- if the private key is not valid- See Also:
-
getVerifier
Get an initializedSignature
for verification. The caller should use theSignature
methods namedupdate
andverify
to verify the signature.- Returns:
- an initialized
Signature
; null if there this object was not created with a public key - Throws:
GeneralSecurityException
- if the public key is not valid- See Also:
-
createPassword
public char[] createPassword(Certificate cert, char[] password) throws GeneralSecurityException, UnsupportedEncodingException Create a password based on digital signatures or message digests. This method should not be used when the user-supplied password is used as is.- Parameters:
cert
- a certificate; null when a certificate is not used.password
- the user-supplied password- Returns:
- the encoded password
- Throws:
GeneralSecurityException
UnsupportedEncodingException
-
decodePassword
Decode an encoded password represented as a string. This method removes base 64, URL encoding.- Parameters:
password
- the encoded password- Returns:
- the bytes that were base 64, URL encoded
-
decodePassword
public static byte[] decodePassword(byte[] password) Decode an encoded password represented as an array of bytes. THis method removes base 64, URL encoding- Parameters:
password
- the encoded password using the US ASCII subset of UTF-8- Returns:
- the bytes that were base 64, URL encoded
-
decodePassword
public static byte[] decodePassword(char[] password) Decode an encoded password represented as an array of chars. THis method removes base 64, URL encoding- Parameters:
password
- the encoded password using the US ASCII subset of UTF-8- Returns:
- the bytes that were base 64, URL encoded
-
getTimeDiff
public static int getTimeDiff(byte[] sigarray) Given a decoded password, find the time difference between the current time and the password's timestamp. A small negative value can occur when clocks are not synchronized. When the exact time is used, the value should be non-negative as the password will be checked after it is created.- Parameters:
sigarray
- the decoded password- Returns:
- the difference between the current time and the password's timestamp in units of seconds
- See Also:
-
checkPassword
public boolean checkPassword(byte[] sigarray, Certificate cert, String password) throws GeneralSecurityException Determine if a password is valid.- Parameters:
sigarray
- the decoded passwordcert
- the certificate used when a password is created; null for digest authentication- Returns:
- true if the password is valid; false otherise
- Throws:
GeneralSecurityException
- See Also:
-
getPEMStrings
Get PEM-encoded strings representing public and private keys with the signature algorithm as a header.- Returns:
- an array whose first element contains the PEM-encoded private key and whose second element contsins the PEM encoded public key, with either null if the key is not present
-
createPEMPair
Generate two PEM encoded strings for a key pair, each with a header specifying a signature algorithm. The header will be "signature-algorithm: SHA256withECDSA" and the type of the PEM headers will be "EC PRIVATE KEY" and "EC PUBLIC KEY".- Returns:
- an array whose first element contains the PEM-encoded private key and whose second element contsins the PEM encoded public key
-
createPEMPair
public static String[] createPEMPair(String pspec, String sigalg) throws IllegalArgumentException, GeneralSecurityException Generate two PEM encoded strings for a key pair, specifying an elliptic curve and a signature algorithm. The header will be "signature-algorithm: " followed by the name of the signature algorithm, and the type of the PEM headers will be "EC PRIVATE KEY" and "EC PUBLIC KEY". The elliptic curve names and the signature-algorithm names are those recognized by Java.- Parameters:
pspec
- the name of the elliptic curve, null for the default (secp256r1)sigalg
- the name of the signature algorithm, null for the default (SHA256withECDSA)- Returns:
- an array whose first element contains the PEM-encoded private key and whose second element contsins the PEM encoded public key
- Throws:
IllegalArgumentException
GeneralSecurityException
-
createPEMPair
public static String[] createPEMPair(File keystoreFile, String pspec, String sigalg, String alias, String dn, char[] pwarray) throws IllegalArgumentException, GeneralSecurityException, IOException Generate two PEM encoded strings for a key pair, specifying an elliptic curve and a signature algorithm, storing the keys in a PKCS #12 file. The header will be "signature-algorithm: " followed by the name of the signature algorithm, and the type of the PEM headers will be "EC PRIVATE KEY" and "EC PUBLIC KEY". The elliptic curve names and the signature-algorithm names are those recognized by Java.- Parameters:
keystoreFile
- the PKCS #12 file, which will be created if necessarypspec
- the name of the elliptic curve, null for the default (secp256r1)sigalg
- the name of the signature algorithm, null for the default (SHA256withECDSA)alias
- an identifier used to name the keydn
- a distinguished name for the certificate corresponding to the public key (for example, CN=nobody@nowhere.com).pwarray
- a character array containing the password to use for the key-store file and to recover the private key given the alias- Returns:
- an array whose first element contains the PEM-encoded private key and whose second element contsins the PEM encoded public key
- Throws:
IllegalArgumentException
GeneralSecurityException
IOException
-