500 likes | 662 Views
An example using t he SUN provider. The provider SUN is supplied i n J ava 2SDK . SUN provides both an implementation of the NIST Digital Signature Algorithm (DSA), and an implementation of the MD5 and NIST SHA-1 message digest algorithms. Class MessageDigest :
E N D
An example using the SUN provider The provider SUNis supplied in Java 2SDK. SUN provides both an implementation of the NIST Digital Signature Algorithm (DSA), and an implementation of the MD5 and NIST SHA-1 message digest algorithms. ClassMessageDigest: looking at code that generates a message digest from a message. MessageDigest messagedigest = MessageDigest.getInstance("SHA");MessageDigest messagedigest = MessageDigest.getInstance("SHA", "SUN"); getInstance() method can be used in two different way. The first requires only the algorithm to be specified. The second requires both the algorithm and the provider to be specified. Both return an instance of a class that implements the SHA algorithm.
Next, we pass the message through the message-digest generator. int n = 0; byte [] rgb = new byte [1000]; while ((n = inputstreamMessage.read(rgb)) > -1) {messagedigest.update(rgb, 0, n); } • This code works well for large messages of unknown length. • The update() method also accepts a single byte as an argument for messages of a few bytes in length, and • a byte array for messages of a fixed or predictable size. • The final step involves generating the message digest itself. rgb = messagedigest.digest(); The resulting digest is encoded in an array of bytes.
Message Digest Algorithm • A message digest algorithm computes a (typically shorter) fixed-size string from a message called the message digest (also known as a digital fingerprint). • Any change to the original message will result in a different message digest, providing a way to verify the integrity (that is, check the fingerprint) of the original message. The message digest algorithm creates amessage digest from a message.
The Complete Source Code for a program that Generates a Message
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.io.InputStream; import java.io.OutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; Publicclass MessageDigestGenerator { publicvoid generateMessageDigest(InputStream inputstreamMessage, OutputStream outputstreamMessageDigest) throws NoSuchAlgorithmException, IOException { MessageDigest messagedigest = MessageDigest.getInstance("SHA"); int n = 0; byte [] rgb = new byte [1000]; while ((n = inputstreamMessage.read(rgb)) > -1) { messagedigest.update(rgb, 0, n); }
rgb = messagedigest.digest(); outputstreamMessageDigest.write(rgb); } public static void main(String [] rgstring) { try { FileInputStream fileinputstream = new FileInputStream(rgstring[0]); FileOutputStream fileoutputstream = new FileOutputStream(rgstring[1]); new MessageDigestGenerator().generateMessageDigest(fileinputstream, fileoutputstream); fileinputstream.close(); fileoutputstream.close(); } catch (Exception ex) { ex.printStackTrace(); } } }
Classes InputStream & OutputStream • public abstract class InputStreamextends Object • This abstract class is the superclass of all classes representing an input stream of bytes. • Applications that need to define a subclass of InputStream must always provide a method that returns the next byte of input. • public abstract class OutputStreamextends Object • This abstract class is the superclass of all classes representing an output stream of bytes. • An output stream accepts output bytes and sends them to some sink. • Applications that need to define a subclass of OutputStream must always provide at least a method that writes one byte of output.
InputStream class: read method detailed • intread(byte[] b) • Reads some number of bytes from the input stream and stores them into the buffer array b. • The number of bytes actually read is returned as an integer. • This method blocks until input data is available, end of file is detected, or an exception is thrown. • If b is null, a NullPointerException is thrown • .If the length of b is zero, then no bytes are read and 0 is returned; • otherwise, there is an attempt to read at least one byte. • If no byte is available because the stream is at end of file, the value -1 is returned; • otherwise, at least one byte is read and stored into b. • Parameter: b - the buffer into which the data is read. • Returns: the total number of bytes read into the buffer, or -1 is there is no more data because the end of the stream has been reached. • Throws: IOException - if an I/O error occurs. NullPointerException- if b is null.
OutputStream class: write method detailed • voidwrite(byte[] b) • Writes b.length bytes from the specified byte array to this output stream. • The general contract for write(b) is that it should have exactly the same effect as the call • write(b, 0, b.length). • Parameters: b - the data. • Throws:IOExceptionif an I/O error occurs. • Class IOException • java.lang.Object • java.lang.Throwable • java.lang.Exception java.io.IOException
Class FileInputStream • public class FileInputStream extends InputStream • A FileInputStream obtains input bytes from a file in a file system. • What files are available depends on the host environment. • FileInputStream is meant for reading streams of raw bytes such as image data. • For reading streams of characters, consider using FileReader.
FileInputStreamConstructor • public FileInputStream(Stringname) throws FileNotFoundException • Creates a FileInputStream by opening a connection to an actual file, the file named by the path name name in the file system. • A new FileDescriptor object is created to represent this file connection. • First, if there is a security manager, its checkRead method is called with the name argument as its argument. • If the named file does not exist, is a directory rather than a regular file, or for some other reason cannot be opened for reading then a FileNotFoundException is thrown.
Class FileOutputStream • public class FileOutputStreamextends OutputStream • A file output stream is an output stream for writing data to a File or to a FileDescriptor. • Whether or not a file is available or may be created depends upon the underlying platform. • Some platforms, in particular, allow a file to be opened for writing by only one FileOutputStream (or other file-writing object) at a time. • In such situations the constructors in this class will fail if the file involved is already open. • FileOutputStream is meant for writing streams of raw bytes such as image data. • For writing streams of characters, consider using FileWriter.
FileOutputStreamConstructor • public FileOutputStream(Stringname) throws FileNotFoundException • Creates an output file stream to write to the file with the specified name. • A new FileDescriptor object is created to represent this file connection. • First, if there is a security manager, its checkWrite method is called with name as its argument. • Parameters:name - the system-dependent filename • Throws:FileNotFoundException if the file exists but is a directory rather than a regular file, if the file does not exist but cannot be created, or if the file cannot be opened for any other reason SecurityException if a security manager exists and its checkWrite method denies write access to the file.
Key in Cryptographic Circles • Akeyis a piece of information used to encrypt and/or decrypt information • There are two types of key-based cryptography: secret-keyand public-key. • Secret-key cryptography uses a single key that both encrypts and decrypts the information to be protected. • Both the sender and the receiver must share the secret-key. • Secret-key cryptography is also known as symmetric cryptography. • Public-key cryptography uses two keys: a public key and a private key. • One key decrypts information encrypted with the other. • Only the private key must be protected. • Public key cryptography is used for authentication.
Class KeyPairGenerator • To generate a digital signature (and encrypt data), we need keys. • Key generation, in its algorithm-independent form, is not substantially similar to creating and using a message digest. KeyPairGenerator keypairgenerator = KeyPairGenerator.getInstance("DSA"); • this code creates an instance of a class that generates DSA-compatible keys. • A second (if necessary) argument specifies the provider.
After a key-pair generator instance is created, it must be initialized. • We can initialize key-pair generators in one of two ways: algorithm-independentor algorithm-dependent. • Which method we use depends on the amount of control we want over the final result. keypairgenerator.initialize(1024, newSecureRandom()); • Keys based on different algorithms differ in how they're generated, but they have one parameter in common -- the key's strength. • Strength is a relative term that corresponds roughly to how hard the key will be to "break." • If we use the algorithm-independent initializer,we can specify only the strength -- any algorithm-dependent values assume reasonable defaults.
DSAKeyPairGenerator dsakeypairgenerator = (DSAKeyPairGenerator) keypairgenerator; DSAParams dsaparams = new DSAParams() { private BigInteger p = BigInteger(...); private BigInteger q = BigInteger(...); private BigInteger g = BigInteger(...); public BigInteger getP() { return p; } public BigInteger getQ() { return q; } public BigInteger getG() { return g; } };dsakeypairgenerator.initialize(dsaparams, new SecureRandom());
While the defaults are usually good enough, if we need more control, it is available. • Let's assume we used the engine to create a generator of DSA-compatible keys, as in the code above. • The engine loaded and instantiated an instance of a class that implements the DSAKeyPairGenerator interface. • If we cast the generic key-pair generator we received to DSAKeyPairGenerator, we then gain access to the algorithm-dependent method of initialization. • To initialize a DSA key-pair generator, we need three values: the prime P, the subprime Q, and the base G. • These values are captured in an inner class instance that is passed to the initialize() method. • The SecureRandom class provides a secure source of random numbers used in the key-pair generation. return keypairgenerator.generateKeyPair(); • The final step involves generating the key pair itself.
The Complete Source Code for a program that Generates a Key Pair.
import java.security.Key; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.IOException; publicclass KeyTools{ public staticvoid writeToFile(Key key, File file)throws IOException{ FileOutputStream fileoutputstream = new FileOutputStream(file); ObjectOutputStream objectoutputstream = new ObjectOutputStream(fileoutputstream); objectoutputstream.writeObject(key); objectoutputstream.close(); }
public staticKey readFromFile(File file) throws ClassNotFoundException, IOException { FileInputStream fileinputstream = new FileInputStream(file); ObjectInputStream objectinputstream = new ObjectInputStream(fileinputstream); Key key = (Key)objectinputstream.readObject(); objectinputstream.close(); return key; } public static void writeToStream(Key key, OutputStream outputstream) throws IOException { new ObjectOutputStream(outputstream).writeObject(key); } public static Key readFromStream(InputStream inputstream) throws ClassNotFoundException,IOException { return (Key) new ObjectInputStream(inputstream).readObject(); }
publicstatic KeyPair generateKeyPair() throws NoSuchAlgorithmException { KeyPairGenerator keypairgenerator = KeyPairGenerator.getInstance("DSA"); keypairgenerator.initialize(1024, new SecureRandom()); return keypairgenerator.generateKeyPair(); } publicstatic void main(String [] rgstring) { try { File filePublic = new File(rgstring[0]); File filePrivate = new File(rgstring[1]); KeyPair keypair = generateKeyPair(); writeToFile(keypair.getPublic(), filePublic); writeToFile(keypair.getPrivate(), filePrivate); } catch (Exception ex) { ex.printStackTrace(); } } }
Class ObjectOutputStream • An ObjectOutputStream writes primitive data types and graphs of Java objects to an OutputStream. public class ObjectOutputStreamextendsOutputStream • For example to write an object that can be read by the example in ObjectInputStream: FileOutputStream fos = new FileOutputStream("t.tmp"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeInt(12345); oos.writeObject("Today"); oos.writeObject(new Date()); oos.close();
Only objects that support thejava.io.Serializable interface can be written to streams. • The class of each serializable object is encoded including the class name and signature of the class, the values of the object's fields and arrays, and the closure of any other objects referenced from the initial objects. • The method writeObject is used to write an object to the stream. • Any object, including Strings and arrays, is written with writeObject. • Multiple objects or primitives can be written to the stream. • The objects must be read back from the corresponding ObjectInputstream with the same types and in the same order as they were written
A digital signature is also generated from a message. • It differs from a message digest because the private key of the message generator is incorporated into the computation. • The result is a message that has been "signed" by the one who holds the private key. • The computation is carried out in such a way that anyone can use the message generator'spublic key to verify that the entity signed it. • A good digital signature algorithm guarantees that the digital signature can't be forged (assuming the private key is secret), • that the signature is good for only the message from which it was generated, • that the message cannot be changed without invalidating the signature • that the message's authenticity can be verified.
The digital signature algorithm creates a digital signature from a message and a private key.
Class Signature • The creation and use of an instance of the Signature class is also similiar to the two previous examples. • The differences lie in how the instance is used either • to sign or • to verify a message. Signature signature = Signature.getInstance("DSA"); • Firstly, we use the engine to get an instance of the appropriate type. • What we do next depends on whether or not we are signing or verifying a message. signature.initSign(privatekey); • In order to sign a message, we must first initialize the signature instance with the private key of the entity that is signing the message. signature.initVerify(publickey); • In order to verify a message, we must initialize the signature instance with the public key of the entity that claims it signed the message.
int n = 0; byte [] rgb = new byte [1000]; while ((n = inputstreamMessage.read(rgb)) > -1) { signature.update(rgb, 0, n); } • Next, regardless of whether or not we are signing or verifying, we must pass the message through the signature generator. • This process is similiar to the earlier example of generating a message digest. • The final step consists of generating the signature or verifying a signature. rgb = signature.sign(); • If we are signing a message, the sign() method returns the signature. signature.verify(rgbSignature); • If we are verifying the signature previously generated from a message, • we must use the verify() method. • It takes as a parameter the previously generated signature and determines whether or not it is still valid.
import java.security.Signature; import java.security.PrivateKey; import java.security.NoSuchAlgorithmException; import java.security.InvalidKeyException; import java.security.SignatureException; import java.io.File; import java.io.InputStream; import java.io.FileInputStream; import java.io.IOException; publicclass Sign { public static byte [] generateSignature(PrivateKey privatekey, InputStream inputstreamMessage) throws NoSuchAlgorithmException, nvalidKeyException,SignatureException, IOException { Signature signature = Signature.getInstance("DSA"); signature.initSign(privatekey); int n = 0; byte [] rgb = new byte [1000]; while ((n = inputstreamMessage.read(rgb)) > -1) { signature.update(rgb, 0, n); } rgb = signature.sign(); return rgb; }
public static voidmain(String [] rgstring) { try { File filePrivate = new File(rgstring[0]); File fileMessage = new File(rgstring[1]); File fileSignature = new File(rgstring[2]); PrivateKey privatekey = (PrivateKey)KeyTools.readFromFile(filePrivate); FileInputStream fileinputstream = new FileInputStream(fileMessage); byte [] rgb = generateSignature(privatekey, fileinputstream); fileinputstream.close(); SignatureTools.writeToFile(rgb, fileSignature); } catch (Exception ex) { ex.printStackTrace(); } } }
The Complete Source Code for a program that Verifies a Message.
import java.security.Signature; import java.security.PublicKey; import java.security.NoSuchAlgorithmException; import java.security.InvalidKeyException; import java.security.SignatureException; import java.io.File; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; publicclass Verify{ public static boolean verifySignature(PublicKey publickey, Input Stream inputstreamMessage, byte [] rgbSignature) throwsNoSuchAlgorithmException,InvalidKeyException, SignatureException,IOException { Signature signature = Signature.getInstance("DSA"); signature.initVerify(publickey); int n = 0; byte [] rgb = new byte [1000]; while ((n = inputstreamMessage.read(rgb)) > -1) { signature.update(rgb, 0, n); } return signature.verify(rgbSignature); }
public static void main(String [] rgstring) {try { File filePublic = new File(rgstring[0]); File fileMessage = new File(rgstring[1]); File fileSignature = new File(rgstring[2]); PublicKey publickey = (PublicKey)KeyTools.readFromFile(filePublic); FileInputStream fileinputstream = new FileInputStream(fileMessage); byte [] rgb = SignatureTools.readFromFile(fileSignature); if (verifySignature(publickey, fileinputstream, rgb)) { System.out.println("true"); } else { System.out.println("false"); } fileinputstream.close(); } catch (Exception ex) { ex.printStackTrace(); } }}
The JCA conveniently hides all the low-level implementation and algorithm-specific details, allowing you to work at a higher, more abstract level. Of course, one of the risks of such an abstract approach is the increased likelihood that we won't recognize erroneous output resulting from bugs. Given the role of cryptography, this can be a significant problem. Consider the "off-by-one" bug in the update line below: int n = 0; byte [] rgb = new byte [1000]; while ((n = inputstreamMessage.read(rgb)) > -1) { messagedigest.update(rgb, 0, n - 1); }
SUN version 1.5 sun.security.provider.Sun • SunRsaSign version1.5 sun.security.rsa.SunRsaSign • SunJSSE version1.5 com.sun.net.ssl.internal.ssl.Provider • SunJCE version1.5 com.sun.crypto.provider.SunJCE • SunJGSS version 1.0 sun.security.jgss.SunProvider • SunSASL version 1.5 com.sun.security.sasl.Provider • SunDeploy-MSCrypto 1.5 version com.sun.deploy.security.MSCryptoProvider
package sun.security.provider; import java.io.*; import java.util.*;import java.security.*; public final class Sun extends Provider { private static final String INFO = "SUN " + "(DSA key/parameter generation; DSA signing; " + "SHA-1, MD5 digests; SecureRandom; X.509 certificates; JKS keystore)"; public Sun() {/* We are the SUN provider */ super("SUN", 1.2, INFO); AccessController.doPrivileged(newjava.security.PrivilegedAction() { public Object run() { /* * Signature engines */ put("Signature.SHA1withDSA", "sun.security.provider.DSA"); put("Alg.Alias.Signature.DSA", "SHA1withDSA"); put("Alg.Alias.Signature.DSS", "SHA1withDSA"); put("Alg.Alias.Signature.SHA/DSA", "SHA1withDSA"); put("Alg.Alias.Signature.SHA-1/DSA", "SHA1withDSA"); put("Alg.Alias.Signature.SHA1/DSA", "SHA1withDSA"); put("Alg.Alias.Signature.SHAwithDSA", "SHA1withDSA"); put("Alg.Alias.Signature.DSAWithSHA1", "SHA1withDSA"); put("Alg.Alias.Signature.OID.1.2.840.10040.4.3", "SHA1withDSA"); put("Alg.Alias.Signature.1.2.840.10040.4.3", "SHA1withDSA"); put("Alg.Alias.Signature.1.3.14.3.2.13", "SHA1withDSA"); put("Alg.Alias.Signature.1.3.14.3.2.27", "SHA1withDSA");
* Key Pair Generator engines */ put(“KeyPairGenerator.DSA", "sun.security.provider.DSAKeyPairGenerator"); put("Alg.Alias.KeyPairGenerator.OID.1.2.840.10040.4.1", "DSA"); put("Alg.Alias.KeyPairGenerator.1.2.840.10040.4.1", "DSA"); put("Alg.Alias.KeyPairGenerator.1.3.14.3.2.12", "DSA"); /* * Digest engines */ put("MessageDigest.MD5", "sun.security.provider.MD5"); put("MessageDigest.SHA", "sun.security.provider.SHA"); put("Alg.Alias.MessageDigest.SHA-1", "SHA"); put("Alg.Alias.MessageDigest.SHA1", "SHA"); /* * Algorithm Parameter Generator engines */ put("AlgorithmParameterGenerator.DSA", "sun.security.provider.DSAParameterGenerator"); /* * Algorithm Parameter engines */ put("AlgorithmParameters.DSA", "sun.security.provider.DSAParameters"); put("Alg.Alias.AlgorithmParameters.1.3.14.3.2.12", "DSA"); put("Alg.Alias.AlgorithmParameters.1.2.840.10040.4.1", "DSA"); /* * Key factories */ put(“KeyFactory.DSA", "sun.security.provider.DSAKeyFactory"); put("Alg.Alias.KeyFactory.1.3.14.3.2.12", "DSA"); put("Alg.Alias.KeyFactory.1.2.840.10040.4.1", "DSA");
/* * SecureRandom */ put("SecureRandom.SHA1PRNG", "sun.security.provider.SecureRandom"); /* * Certificates */ put("CertificateFactory.X509", "sun.security.provider.X509Factory"); put("Alg.Alias.CertificateFactory.X.509", "X509"); /* * KeyStore */ put("KeyStore.JKS", "sun.security.provider.JavaKeyStore"); /* * KeySize */ put("Signature.SHA1withDSA KeySize", "1024"); put("KeyPairGenerator.DSA KeySize", "1024"); put("AlgorithmParameterGenerator.DSA KeySize", "1024"); /* * Implementation type: software or hardware */ put("Signature.SHA1withDSA ImplementedIn", "Software"); put("KeyPairGenerator.DSA ImplementedIn", "Software"); put("MessageDigest.MD5 ImplementedIn", "Software"); put("MessageDigest.SHA ImplementedIn", "Software"); put("AlgorithmParameterGenerator.DSA ImplementedIn", "Software"); put("AlgorithmParameters.DSA ImplementedIn", "Software"); put("KeyFactory.DSA ImplementedIn", "Software"); put("SecureRandom.SHA1PRNG ImplementedIn", "Software"); put("CertificateFactory.X509 ImplementedIn", "Software"); put(“KeyStore.JKS ImplementedIn", "Software"); return null; } })
Example: Java Security Providers • Thefollowing Java2 applet enumerates all the Java Security Providers available to the j2re and their associated properties and values. • These property values specify the engine.algorithms and the classes that implement them, as well as other properties. • The public method of the applet is accessed by a scripted call to the method from JavaScript. • The method returns the data in a formatted table string. • A JavaScript function embeds the table of results into a simple html page in another scripted window.
import java.io.*; import java.util.*; import java.awt.* ; import java.security.*; public class SecProviders extends java.applet.Applet{ public void init() { } // end init() public String getSecurityProviders() { StringBuffer strbuff = new StringBuffer(10000) ; //typical size of buffer to hold html string output try { Provider p[] = Security.getProviders(); strbuff.append("<table border=1 cellpadding=3>"); for (int i = 0; i < p.length; i++) { strbuff.append("<tr><td bgcolor=blue colspan=2><font size=+1 color=yellow><b>" + p[i] + " " + p[i].getClass().getName() + "</b></font></tr>\r\n") ;
for (Enumeration e = p[i].keys(); e.hasMoreElements();) { String key = (String) e.nextElement() ; strbuff.append("<tr><td>" + key + "</td><td>" + p[i].getProperty(key) + "</td></tr>\r\n") ; } } strbuff.append("</table>\r\n") ; return strbuff.toString(); } catch (Exception e) { return e.toString(); } } }
The subclasses of the Provider class For each service implemented by theprovider, there must be a property whosename is the type of service: • Signature.algName [one or more spaces] attrName • MessageDigest.algName [one or more spaces] attrName • KeyPairGenerator.algName [one/more spaces] attrName • SecureRandom.algName [one /spaces] attrName • KeyFactory.algName [one / spaces] attrName • CertificateFactory.certType [one or more spaces] attrName • KeyStore.storeType [one or more spaces] attrName • AlgorithmParameterGenerator.algName [one / spaces] attrName • AlgorithmParameters.algName [one / more spaces] attrName
Java Security Provider Examples • The default provider "SUN" implements the SHA1withDSA Digital Signature Algorithm in software. • In the master class for the provider "SUN", it sets the "Signature.SHA1withDSA ImplementedIn" to have the value "Software" via the following: put("Signature.SHA1withDSAImplementedIn", "Software") • The default provider "SUN" implements the Digital Signature Algorithm (whose standard name is "SHA1withDSA") in a class named DSA in the sun.security.provider package. • Its subclass of Provider sets the Signature.SHA1withDSA property to have the value "sun.security.provider.DSA" via the following: put("Signature.SHA1withDSA", "sun.security.provider.DSA")
getProvidersmethod • public static Provider[]getProviders() • Returns an array containing all the installed providers. The order of the providers in the array is their preference order. • Returnsan array of all the installed providers.
getProvidermethod • public static ProvidergetProvider(Stringname) • Returns the provider installed with the specified name, if any. • Returns null if no provider with the specified name is installed. • Parameters: name - the name of the provider to get. • Returns:he provider of the specified name.
get Providers method • public static Provider[]getProviders(Mapfilter) • Returns an array containing all installed providers that satisfy the specified selection criteria, or • Returns null if no such providers have been installed. • The returned providers are ordered according to their preference order • Parameters:filter - the criteria for selecting providers. The filter is case-insensitive. • Returns: all the installed providers that satisfy the selection criteria, or null if no such providers have been installed. • Throws: InvalidParameterException - if the filter is not in the required format • The selection criteria are represented by a map. • Each map entry represents a selection criterion. • A provider is selected iff it satisfies all selection criteria.
The key entry • The key for any entry in such a map must be in one of the following two formats: <crypto_service>.<algorithm_or_type> MessageDigest.SHA-384 , MessageDigest.MD5 , • The cryptographic service name must not contain any dots. • The value associated with the key must be an empty string. • A provider satisfies this selection criterion iff the provider implements the specified algorithm or type for the specified cryptographic service. <crypto_service>.<algorithm_or_type> <attribute_name> Provider.id className, Provider.id version , KeyStore.JKS ImplementedIn , MessageDigest.SHA ImplementedIn , KeyFactory.DSA ImplementedIn • The cryptographic service name must not contain any dots. • The value associated with the key must be a non-empty string. • A provider satisfies this selection criterion iff the provider implements the specified algorithm or type for the specified cryptographic service and its implementation meets the constraint expressed by the specified attribute name/value pair.