Szymon Starzycki | 781f9fc | 2013-10-02 17:21:41 -0700 | [diff] [blame] | 1 | package signtool; |
| 2 | |
| 3 | import java.io.*; |
| 4 | import java.util.Properties; |
| 5 | import java.util.ArrayList; |
| 6 | |
| 7 | import javax.mail.internet.*; |
| 8 | import javax.mail.MessagingException; |
| 9 | import javax.mail.Session; |
| 10 | import javax.activation.MailcapCommandMap; |
| 11 | import javax.activation.CommandMap; |
| 12 | |
| 13 | import java.security.PrivateKey; |
| 14 | import java.security.Security; |
| 15 | import java.security.KeyFactory; |
| 16 | import java.security.KeyStore; |
| 17 | import java.security.NoSuchAlgorithmException; |
| 18 | import java.security.spec.PKCS8EncodedKeySpec; |
| 19 | import java.security.spec.InvalidKeySpecException; |
| 20 | import java.security.cert.X509Certificate; |
| 21 | import java.security.cert.CertificateFactory; |
| 22 | import java.security.cert.Certificate; |
| 23 | import java.security.cert.CertificateException; |
| 24 | import java.security.cert.CertificateEncodingException; |
| 25 | |
| 26 | import org.bouncycastle.jce.provider.BouncyCastleProvider; |
| 27 | import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; |
| 28 | import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; |
| 29 | import org.bouncycastle.operator.ContentSigner; |
| 30 | import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; |
| 31 | import org.bouncycastle.cms.CMSProcessableByteArray; |
| 32 | import org.bouncycastle.cms.CMSSignedGenerator; |
| 33 | import org.bouncycastle.cms.CMSSignedDataGenerator; |
| 34 | import org.bouncycastle.cms.CMSSignedGenerator; |
| 35 | import org.bouncycastle.cms.CMSProcessable; |
| 36 | import org.bouncycastle.cms.CMSSignedData; |
| 37 | import org.bouncycastle.cms.CMSTypedData; |
| 38 | import org.bouncycastle.cert.jcajce.JcaCertStore; |
| 39 | import org.bouncycastle.util.Store; |
| 40 | import org.bouncycastle.asn1.ASN1InputStream; |
| 41 | import org.bouncycastle.asn1.DEROutputStream; |
| 42 | import org.bouncycastle.asn1.ASN1Object; |
| 43 | |
| 44 | |
| 45 | public class SignImg { |
| 46 | |
| 47 | /* It reads private key in pkcs#8 formate |
| 48 | * Conversion: |
| 49 | * openssl pkcs8 -topk8 -nocrypt -outform DER < inkey.pem > outkey.pk8 |
| 50 | */ |
| 51 | private static PrivateKey getPrivateKey(String path) throws IOException, FileNotFoundException, NoSuchAlgorithmException, InvalidKeySpecException { |
| 52 | File file = new File(path); |
| 53 | FileInputStream fis = new FileInputStream(file); |
| 54 | byte[] data = new byte[(int)file.length()]; |
| 55 | fis.read(data); |
| 56 | fis.close(); |
| 57 | |
| 58 | PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(data); |
| 59 | KeyFactory kf = KeyFactory.getInstance("RSA"); |
| 60 | PrivateKey privateKey = kf.generatePrivate(kspec); |
| 61 | |
| 62 | return privateKey; |
| 63 | } |
| 64 | |
| 65 | private static MimeBodyPart getContent(String path) throws IOException, FileNotFoundException, MessagingException { |
| 66 | MimeBodyPart body = new MimeBodyPart(); |
| 67 | |
| 68 | File file = new File(path); |
| 69 | FileInputStream fis = new FileInputStream(file); |
| 70 | byte[] data = new byte[(int)file.length()]; |
| 71 | fis.read(data); |
| 72 | fis.close(); |
| 73 | |
| 74 | body.setContent(data, "application/octet-stream"); |
| 75 | |
| 76 | return body; |
| 77 | } |
| 78 | |
| 79 | private static CMSProcessableByteArray getCMSContent(String path) throws IOException, FileNotFoundException, MessagingException { |
| 80 | File file = new File(path); |
| 81 | FileInputStream fis = new FileInputStream(file); |
| 82 | byte[] data = new byte[(int)file.length()]; |
| 83 | fis.read(data); |
| 84 | fis.close(); |
| 85 | CMSProcessableByteArray cms = new CMSProcessableByteArray(data); |
| 86 | |
| 87 | return cms; |
| 88 | } |
| 89 | |
| 90 | private static X509Certificate readCert(String path) throws IOException, FileNotFoundException, CertificateException { |
| 91 | File file = new File(path); |
| 92 | FileInputStream is = new FileInputStream(file); |
| 93 | |
| 94 | CertificateFactory cf = CertificateFactory.getInstance("X.509"); |
| 95 | Certificate cert = cf.generateCertificate(is); |
| 96 | is.close(); |
| 97 | |
| 98 | return (X509Certificate) cert; |
| 99 | } |
| 100 | |
| 101 | private static void save(MimeBodyPart content, String path) throws IOException, FileNotFoundException, MessagingException { |
| 102 | File file = new File(path); |
| 103 | FileOutputStream os = new FileOutputStream(file); |
| 104 | |
| 105 | content.writeTo(os); |
| 106 | |
| 107 | os.close(); |
| 108 | } |
| 109 | |
| 110 | private static Store certToStore(X509Certificate certificate) throws CertificateEncodingException { |
| 111 | ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>(); |
| 112 | certList.add(certificate); |
| 113 | return new JcaCertStore(certList); |
| 114 | } |
| 115 | |
| 116 | public static void setDefaultMailcap() |
| 117 | { |
| 118 | MailcapCommandMap _mailcap = |
| 119 | (MailcapCommandMap)CommandMap.getDefaultCommandMap(); |
| 120 | |
| 121 | _mailcap.addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature"); |
| 122 | _mailcap.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime"); |
| 123 | _mailcap.addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature"); |
| 124 | _mailcap.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime"); |
| 125 | _mailcap.addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed"); |
| 126 | |
| 127 | CommandMap.setDefaultCommandMap(_mailcap); |
| 128 | } |
| 129 | |
| 130 | public static void main(String[] args) { |
| 131 | try { |
| 132 | if (args.length < 4) { |
| 133 | System.out.println("Usage: signimg data private_key certificate output"); |
| 134 | return; |
| 135 | } |
| 136 | System.out.println("Signing the image"); |
| 137 | setDefaultMailcap(); |
| 138 | |
| 139 | Security.addProvider(new BouncyCastleProvider()); |
| 140 | |
| 141 | PrivateKey key = getPrivateKey(args[1]); |
| 142 | System.out.println("File read sucessfully"); |
| 143 | |
| 144 | CMSSignedDataGenerator generator = new CMSSignedDataGenerator(); |
| 145 | |
| 146 | CMSTypedData body = getCMSContent(args[0]); |
| 147 | System.out.println("Content read sucessfully"); |
| 148 | |
| 149 | X509Certificate cert = (X509Certificate) readCert(args[2]); |
| 150 | System.out.println("Certificate read sucessfully"); |
| 151 | |
| 152 | ContentSigner sha256Signer = new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").build(key); |
| 153 | |
| 154 | Store certs = certToStore(cert); |
| 155 | |
| 156 | generator.addCertificates(certs); |
| 157 | generator.addSignerInfoGenerator( |
| 158 | new JcaSignerInfoGeneratorBuilder( |
| 159 | new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()) |
| 160 | .build(sha256Signer, cert)); |
| 161 | |
| 162 | CMSSignedData signed = generator.generate(body, true); |
| 163 | System.out.println("Signed"); |
| 164 | |
| 165 | Properties props = System.getProperties(); |
| 166 | Session session = Session.getDefaultInstance(props, null); |
| 167 | |
| 168 | File file = new File(args[3]); |
| 169 | FileOutputStream os = new FileOutputStream(file); |
| 170 | |
| 171 | ASN1InputStream asn1 = new ASN1InputStream(signed.getEncoded()); |
| 172 | ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| 173 | DEROutputStream dOut = new DEROutputStream(os); |
| 174 | dOut.writeObject(ASN1Object.fromByteArray(signed.getEncoded())); |
| 175 | |
| 176 | } |
| 177 | catch (Exception ex) { |
| 178 | System.out.println("Exception during programm execution: " + ex.getMessage()); |
| 179 | } |
| 180 | } |
| 181 | } |