They have forgotten to install the unrestricted policy files for the JCE. This happens rather a lot.
The problem is most likely that the application is running out of the JRE installed on the Windows machine, not the JDK. You need to make sure that the JRE is configured the same way as the JDK with the right JAR files installed.
Chances are this is happening because the provider name has not been specified and, because of the precedence rules, some other provider is being used to create the object. Verify that getInstance() has been called with the provider name specified.
The key words here are "padded byte stream." This tells you that the extra zero bytes are most likely an artifact from the padding mechanism that was used to create the encrypted data. A look at the program will probably show that the program was written using the value returned from Cipher.getOutputSize() but does not take into account the fact that the number of bytes returned by Cipher.doFinal() can be less than that.
The initialization vector, or IV, is probably incorrect. Remember, CBC mode is performed by XORing the previous block of ciphertext produced with the current block of input. In the case of the first block of input, the (nonexistent) block of cipher text is provided by the IV.
You can use either Cipher.getIV(), which returns a simple byte array, or Cipher.getParameters() , which returns an AlgorithmParameters object.
You need to use Cipher.getParameters() and pass a PBEParameterSpec class to the AlgorithmParameters.getParameterSpec() method of the object returned by getParameters() .
In the case of truncated data, the most likely problem is that CipherOutputStream.close() has not been called.
Encryption does not necessarily prevent an attacker from tampering with a message.
Use a MAC based on either a message digest or a cipher, whichever is mos convenient while being appropriate to the security needs of the data. The use of a MAC now means that two parties who want to communicate must be aware of the key material used to initialize the MAC.
The primary limitation of a MAC or message digest is the amount of data that it is safe to feed into it. The data limitation exists because, beyond a certain size , the likelihood of different data computing to the same digest or MAC value becomes too high.
cipher.init(Cipher.ENCRYPT_MODE, key); String encrypted = new String(cipher.doFinal(input)); cipher.init(Cipher.DECRYPT_MODE, key); byte[] decrypted = cipher.doFinal(encrypted.getBytes());
What kind of String is created from new String(cipher.doFinal(input)) and what bytes are returned by encrypted.getBytes() is dependent on the default charset used by your Java runtime and will almost always mean that encrypted.getBytes() will not produce the same byte array that came out of cipher.doFinal(input) .
Leading zeros will go missing because the input data has to be converted to a big integer before it can be used with RSA. The solution is to use a padding mechanism like OAEP or PKCS #1.
The answer is to combine the asymmetric algorithm with a symmetric one. Generate a random key for the symmetric algorithm and encrypt the data using it. Then encrypt the symmetric key using the public key for the person you want to send data to. The person can then use the private key to recover the symmetric key and then recover the data using the symmetric key.
Key agreement schemes need to be combined with an authentication scheme to authenticate the keys being used; otherwise , the agreement scheme is open to a "man-in-the-middle" attack.
Digital signatures based on asymmetric algorithms are the best way of dealing with this. The feature of a digital signature is it has to be created using a person's private key, which should be known only to that person and it can be verified using the person's public key, which can be made freely available.
They are left out of the encoding.
MyChoice ::= CHOICE { message UTF8String, id INTEGER }
Here is one way of doing it. As you can see, the implementation reads a lot like a Java version of a C, or Pascal, union. Note the use of the ASN1Choice interface. Use of this will reduce the likelihood that this object is mistakenly tagged implicitly.
public class MyChoice extends ASN1Encodable implements ASN1Choice { ASN1Encodable value; public MyChoice(DERInteger value) { this.value = value; } public MyChoice(DERUTF8String value) { this.value = value; } public boolean isInteger() { return (value instanceof DERInteger); } public DERUTF8String getMessage() { if (isInteger()) { throw new IllegalStateException("not a message!"); } return (DERUTF8String)value; } public DERInteger getId() { if (isInteger()) { return (DERInteger)value; } throw new IllegalStateException("not an id!"); } public DERObject toASN1Object() { return value.toASN1Object(); } }
Objects that are assigned a tag value using the implicit style have their own tag value overridden by the tag value assigned. In the Bouncy Castle API, this is done by setting the explicit parameter in the tagged object constructor to false . For example, the ASN.1 declaration
value [0] IMPLICIT INTEGER
would be created as a DER tagged value using the following:
DERTaggedObject t = new DERTaggedObject(false, derIntegerValue);
and then recovered using the following:
derIntegerValue = DERInteger.getInstance(t, false);
Remember too that if an object is already tagged, implicitly tagging it will remove the tag value. For this reason, as they commonly contain tagged values, any tag applied to an item of type CHOICE is applied explicitly.
The X509EncodedKeySpec is used to contain public keys. The PKCS8EncodedKeySpec is used for carrying the encodings of private keys. The KeyFactory class is used to take the information in the encoded key specification objects and produce actual keys.
An EncryptedPrivateKeyInfo contains an encrypted encoding of a PKCS #8 PrivateKeyInfo object and the parameter and algorithm details that were used to do the encryption. On decryption, the recovered information is used to create a PKCS8EncodedKeySpec, which is then used to create a private key.
Some application software expects the X.500 name to have a specific encoding when it processes it, and converting from an encoded DN to String and back can lose this information. The reason this happens is that while several ASN.1 string types can be transformed into a Java String trivially, there is no way to determine which of those ASN.1 string types were used originally if you want to transform back the other way.
You should add the e-mail address to a SubjectAlt Name extension attached to the certificate. Use the GeneralName choice item for the rfc822 Name.
Create an extensionRequest attribute and add an Extensions structure containing the extensions you want as the contents of the attribute value set. This attribute is then added to the attribute set that is carried with the certification request and processed by the CA.
A certificate path, or chain, represents a linkage from an end entity certificate back to a root certificate where each certificate in the chain is signed by the next one after it. The root certificate is generally selfsigned and has to be accepted on trust. It is the certificate the others derive their validity from. The end entity certificate cannot be used to validate any other members of the chain and is the certificate you are trying to validate when the chain is examined.
Convert the path, or chain, into a CertPath object and then use the CertPath.getEncoded() method requesting the format desired. You can find out which formats the CertPath objects of a particular provider can generate by calling CertPath.getEncodings() .
OCSP. You are most likely to encounter problems with regular CRLs, as the size of the lists being distributed may easily become prohibitive, and having a large number of clients all trying to update their CRLs at the same time may result in severe performance degradation on the CRL server, with the usual unpleasant consequences.
You can distribute deltas as updates, rather than sending out a new CRL. If you do this, the complete CRLs you send out need to have the CRLNumber extension present in them and the delta CRLs you send out need to use the DeltaCRLIndicator extension. Bear in mind that you need to have an algorithm, such as the one given in RFC 3280, for adding the deltas to the earlier complete CRL that was sent.
Validation of private critical certificate extensions can be introduced to a PKIX CertPathValidator by using the PKIXCertPathChecker class. Remember that the checker you write must remove the extensions you are processing from the unresolved critical extensions Set thatis passed to the check() method for the validation to succeed.
Use the issuer and serial number of the target certificate, because these should always uniquely identify the certificate. For example, given the X509Certificate targetCert that is the target of the path build, the target constraints would be defined as
X509CertSelector targetConstraints = new X509CertSelector(); targetConstraints.setSerialNumber( targetCert .getSerialNumber()); targetConstraints.setIssuer( targetCert .getIssuerX500Principal().getEncoded());
Of course, if you are fortunate enough to be using JDK 1.5 or later, you can leave off the getEncoded() .
Of the standard Java keystore types, the JCEKS supports storing symmetric keys. The Bouncy Castle provider also supports symmetric key storage with the BKS and UBER KeyStore type.
Change the default password!
There is no "friendly name " attribute attached to the SafeBag containing the private key. If the file does import and you see a hex string, chances are it will be the value of the local key ID. Some applications stillinsist and having the "friendly name" attribute provided in order to successfully import a PKCS #12 file, so this can sometimes be a cause of failure as well.
The first one is that the PKCS #12 store you are using may have been saved as a BER file, but the application can only import DER files ”you'll need to convert the file. The second one is the PKCS #12 store you are using may allow you to have an integrity password that is different from the privacy password, and the application can deal only with files that have the same password for both integrity and privacy. The third, and last, one is that the application may expect the certificates, other than the trust anchor, making up the validation path for the key's certificate to have the AuthorityKeyIdentifier and SubjectKeyIdentifier extensions present, and if it cannot find them, it is unable to build the validation path and accept the key as usable.
Most of the code for this you can cut and paste, but I'll reproduce one solution here. The first thing I'll do is provide the Java code and then just go through the command sequence.
Here's the Java code for parsing the request and creating the certificate response. It reads the request from a file called pkcs10.req and generates the certificate path making up the response in PKCS7 format in a file called pkcs7.pth .
package chapter8; import java.io.*; import java.math.BigInteger; import java.security.cert.*; import java.util.*; import javax.security.auth.x500.X500PrivateCredential; import org.bouncycastle.asn1.x509.*; import org.bouncycastle.jce.PKCS10CertificationRequest; import org.bouncycastle.openssl.PEMReader; import org.bouncycastle.x509.X509V3CertificateGenerator; import org.bouncycastle.x509.extension.*; /** * Example showing the processing of a PEM encoded PKCS #10 encoded request * in a file called "pkcs10.req". A PKCS7 certificate path is generated as a * response in the file "pkcs7.pth". *
* The certificate and its chain will be valid for 50 seconds. */ public class CertReqSolution { public static void main(String[] args) throws Exception { // create the CA certificates X500PrivateCredential rootCredential = Utils.createRootCredential(); X500PrivateCredential interCredential = Utils.createIntermediateCredential( rootCredential.getPrivateKey(), rootCredential.getCertificate()); // parse the request PEMReader pRd = new PEMReader( new InputStreamReader( new FileInputStream("pkcs10.req"))); PKCS10CertificationRequest request = (PKCS10CertificationRequest)pRd.readObject(); // get our validation certificate X509Certificate caCert = interCredential.getCertificate(); X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis())); certGen.setIssuerDN(caCert.getSubjectX500Principal()); certGen.setNotBefore(new Date(System.currentTimeMillis())); certGen.setNotAfter(new Date(System.currentTimeMillis() + 50000)); certGen.setSubjectDN(request.getCertificationRequestInfo().getSubject()); certGen.setPublicKey(request.getPublicKey("BC")); certGen.setSignatureAlgorithm("SHA256WithRSAEncryption"); // provide some basic extensions and mark the certificate // as appropriate for signing and encipherment certGen.addExtension( X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(caCert)); certGen.addExtension( X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure( request.getPublicKey("BC"))); certGen.addExtension( X509Extensions.BasicConstraints, true, new BasicConstraints(false)); certGen.addExtension( X509Extensions.KeyUsage, true, new KeyUsage( KeyUsage.digitalSignature KeyUsage.keyEncipherment)); // create the chain List chain = Arrays.asList( new Certificate[] { certGen.generateX509Certificate( interCredential.getPrivateKey(), "BC"), interCredential.getCertificate(), rootCredential.getCertificate() }); // create the CertPath CertificateFactory fact = CertificateFactory.getInstance("X.509", "BC"); CertPath path = fact.generateCertPath(chain); // write it out FileOutputStream fOut = new FileOutputStream("pkcs7.pth"); fOut.write(path.getEncoded("PKCS7")); fOut.close(); } }
There's nothing new in this code, although if you do find some of it confusing, it would be worth having another look at the examples and descriptions in Chapter 6 before going any further. Compile up the code into your class hierarchy and you're ready to proceed.
Now to use it with the keytool ”I've used test.jks as the keystore file here so that the commands avoid touching your .keystore file. Remember, both the java code you are using and the keytool will be operating on the current directory.
First, generate a key to use, responding appropriately to the questions the keytool prompts you with:
keytool -genkey -alias testKey -keystore test.jks -storepass testStore -keypass testKey
Next, generate the certification request into the file pkcs10.req :
keytool -certreq -alias testKey -keystore test.jks -storepass testStore -keypass testKey -file pkcs10.req
Next, run the Java program to read the request and generate the response:
java -cp your_class_hierarchy chapter8.CertReqSolution
where your_class_hierarchy is wherever you compiled the class file to.
Finally, import the certificate response:
keytool -import -alias testKey -keystore test.jks -storepass testStore -keypass testKey -file pkcs7.pth
You will be prompted to determine whether you want to trust the root certificate. Respond with a yes and you're done. Do a
keytool -list -v -keystore test.jks -storepass testStore
and you should see the certificate path has now been added to the entry.
Here's one way of doing it:
package chapter9; import java.io.*; import org.bouncycastle.cms.*; /** * CMSProcessable that handles File objects. */ public class CMSProcessableFile implements CMSProcessable { private File file; private static final int BUF_SIZE = 4096; /** * Base constructor. * * @param file a File object representing the file we want processed . */ public CMSProcessableFile(File file) { this.file = file; } /** * Write the contents of the file to the passed in OutputStream * * @param out the OutputStream passed in by the CMS API. */ public void write(OutputStream out) throws IOException, CMSException { FileInputStream fIn = new FileInputStream(file); byte[] buf = new byte[BUF_SIZE]; int count = 0; while ((count = fIn.read(buf)) > 0) { out.write(buf, 0, count); } fIn.close(); } /** * Return the File object we were created with. */ public Object getContent() { return file; } }
One thing to note: Be sure to close the InputStream you are using in the write() method, as I've done here. The write() method can be called multiple times.
Make them purpose-built. That means a certificate is for one purpose only and the public key will be used either for encryption or signature validation. There is an interesting slant on this in Chapter 13 of Practical Cryptography, where the authors point out that for a key to be different, just the public exponent needs to change; the modulus can be reused.
A CMS signeddata message will not contain any signers if it is being used only to carry certificates and/or CRLs. Messages of this type are called certificate management messages and are also created when Cyou encode a CertPath by passing the String to "PKCS7" to the CertPath object's getEncoded() method.
Strictly speaking, you don't have to! Here is the modified method with the changes highlighted:
public static MimeMultipart createMultipartWithSignature( PrivateKey key, X509Certificate cert, CertStore certsAndCRLs, MimeMultipart multiPart) throws Exception { // create some smime capabilities in case someone wants to respond ASN1EncodableVector signedAttrs = new ASN1EncodableVector(); SMIMECapabilityVector caps = new SMIMECapabilityVector(); caps.addCapability(SMIMECapability.aES256_CBC); caps.addCapability(SMIMECapability.dES_EDE3_CBC); caps.addCapability(SMIMECapability.rC2_CBC, 128); signedAttrs.add(new SMIMECapabilitiesAttribute(caps)); signedAttrs.add(new SMIMEEncryptionKeyPreferenceAttribute( SMIMEUtil.createIssuerAndSerialNumberFor(cert))); // set up the generator SMIMESignedGenerator gen = new SMIMESignedGenerator(); gen.addSigner(key, cert, SMIMESignedGenerator.DIGEST_SHA256, new AttributeTable(signedAttrs), null); gen.addCertificatesAndCRLs(certsAndCRLs); MimeBodyPart dataPart = new MimeBodyPart(); dataPart.setContent(multiPart); // create the signed message return gen.generate(dataPart, "BC"); }
As you can see, this could have been more easily achieved by wrapping multiPart before passing it to the method and leaving the method's parameter list alone.
The other application is probably ignoring some of the headers in the mail message when it is doing the signature calculation. Typical headers that are ignored (when it happens) are "Reply-To," "From," and "To."
If you are using a "lossy" compression technique, you have to sign the compressed data rather than the original data. Because this compression technique involves some loss of information, signatures calculated on the original data will not match the results of the verification process at the other end on the reduced data.
The key manager properties are javax.net.ssl.keyStore, javax.net.ssl.keyStorePassword, javax.net.ssl.keyStoreProvider, and javax.net.ssl.keyStoreType . The trust manager properties are javax.net.ssl.trustStore, javax.net.ssl.trustStorePassword, javax.net.ssl.trustStoreProvider, and javax.net.ssl.trustStoreType .
If the javax.net.ssl.trustStore property is not set, the system will look in the JVM's security directory for the file jssecacerts . If jssecacerts does not exist, the system will use the file cacerts .
If the javax.net.ssl.keyStoreType or javax.net.ssl.trustStoreType properties are not set, they default to the return value of KeyStore.getDefaultType() .
Providing the password will force an integrity check on the file and help detect any tampering that might have occurred.
The javax.net.ssl.SSLContext class.
The SSLSocket.startHandshake() method.
Introduction