itext5でPDFを暗号化する

itext5でPDFを暗号化する

PDFファイルを暗号化したかったわけではないが、
暗号化したPDFをフィルタリングする機能を作成したかった。
そこで、サンプルの暗号化されたPDFを探したが、256bitAESで暗号化されたファイルが見つからなかった。
しかたないので、適当なPDFを256bitAES暗号化するプログラムを作成した。

itext5を利用している。
暗号化が関連するため、bcprovが必要になる。

下記の2つのjarファイルをeclipseのbuild pathに追加した。

  • itext5-itextpdf-5.5.12.jar
  • bcprov-jdk15on-158.jar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package test.pdf;

import java.io.FileOutputStream;

import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfWriter;

public class Enc {
    public static void main(String[] args) {
        System.out.println("start.");
        PdfReader reader = null;
        PdfStamper stamper = null;
        try {
            //
            byte[] ownerPassword = args[0].getBytes();
            byte[] userPassword = args[1].getBytes();
            reader = new PdfReader(args[2]);
            //
            stamper = new PdfStamper(reader, new FileOutputStream(args[3]));
            stamper.setEncryption(userPassword, ownerPassword, PdfWriter.ALLOW_SCREENREADERS, PdfWriter.ENCRYPTION_AES_256);
            //
            System.out.println("end.");
        } catch (Exception e) {
            System.out.println("error. " + e.toString());
        } finally {
            try {
                stamper.close();
            } catch (Exception e) {
                System.out.println("stamper close failed. " + e.toString());
            }
            try {
                reader.close();
            } catch (Exception e) {
                System.out.println("reader close failed. " + e.toString());
            }
        }
    }
}

下記の通り実行すると、暗号化されていないPDFを暗号化した新しいファイルが保存される。

1
2
export CLASSPATH=itext5-itextpdf-5.5.12.jar:bcprov-jdk15on-158.jar:.:$CLASSPATH
java test.pdf.Enc 99999 "" orginal.pdf encrypted.pdf

99999は管理者パスワード
“”はユーザパスワードなしを示している
original.pdfを暗号化し、encrypted.pdfとして出力する。

同じタグの記事
同じカテゴリの記事

Oracle MERGE文

OracleのMERGE文

最近では、すっかりおなじみとなったOracleのMERGE文。
条件に一致した場合はUPDATEが発行され、一致しなかった場合はINSERTが発行される。

テーブルからテーブルへ値を移す時に使われるサンプルが多いが、
実際には、プログラムからデータを登録するときにも利用できる。

たとえば、下記のようなテーブルが存在した時、

1
2
3
4
5
6
7
8
CREATE TABLE WK01 (
    CODE CHAR(3)      NOT NULL,
    NAME VARCHAR2(20) NOT NULL,
    CNT  NUMBER(5)    NOT NULL,
    INS  DATE,
    UPD  DATE,
    PRIMARY KEY (CODE)
);

下記のようなMERGEを発行することができる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
MERGE INTO WK01 T1
USING (
    SELECT
        ? AS CODE,
        ? AS NAME
    FROM
        DUAL
) T2
ON (
    T1.CODE = T2.CODE
)
WHEN MATCHED THEN
    UPDATE SET
        NAME = T2.NAME,
        CNT  = CNT + 1,
        UPD  = SYSDATE
WHEN NOT MATCHED THEN
    INSERT (
        CODE,
        NAME,
        CNT,
        INS
    ) VALUES (
        T2.CODE,
        T2.NAME,
        1,
        SYSDATE
    );

?にはバインド変数を設定する。

また、WK01.NAMEにはWK02.NAMEを設定しなくてはならない場合、
USINGにSELECTを追加する。

1
2
3
4
5
CREATE TABLE WK02 (
    CODE CHAR(3)     NOT NULL,
    NAME VARCHAR(20) NOT NULL,
    PRIMARY KEY (CODE)
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
MERGE INTO WK01 T1
USING (
    SELECT
        ? AS CODE,
        (SELECT T3.NAME FROM WK02 T3 WHERE T3.CODE = ?) AS NAME
    FROM
        DUAL
) T2
ON (
    T1.CODE = T2.CODE
)
WHEN MATCHED THEN
    UPDATE SET
        NAME = T2.NAME,
        UPD  = SYSDATE
WHEN NOT MATCHED THEN
    INSERT (
        CODE,
        NAME,
        INS
    ) VALUES (
        T2.CODE,
        T2.NAME,
        SYSDATE
    );

構文はよく忘れるけど、判定無しにupdateとinsertを使い分けてくれるので楽にロジックが組めるようになる。
厳密に、更新と挿入でロジックを分けている場合は、この方法は使えないけれど。

同じタグの記事
同じカテゴリの記事

[java] StandardOpenOption.DELETE_ON_CLOSE

StandardOpenOption.DELETE_ON_CLOSE

java.nio.fileのStandardOpenOptionにはDELETE_ON_CLOSEという定数が定義されている。
この定数をファイルのオープン時のオプションとして利用すると、ファイルをクローズした時に自動的にファイルが削除される。
下記の通り利用する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.io.BufferedWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class DeleteOnCloseTest {
    public static void main(String[] args) {
        Path file1 = Paths.get("C:", "file1.txt");
        try (BufferedWriter writer = Files.newBufferedWriter(file1, StandardCharsets.UTF_8,
             StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING,
             StandardOpenOption.WRITE, StandardOpenOption.DELETE_ON_CLOSE)) {
            writer.write("test1-1\n");
            writer.write("test1-2\n");
            writer.write("test1-3\n");
            System.out.println("1.file1 exists? " + Files.exists(file1));
        } catch (Exception e) {
            System.out.println("create file failed. " + e.toString());
        }
        System.out.println("2.file1 exists? " + Files.exists(file1));
    }
}

実行すると、

1.file1 exists? true
2.file1 exists? false

と出力される。
中間ファイルとして利用するのに便利なのかもしれないが、
もし、出力中に想定外のExceptionが発生した場合は、原因追求のためにファイルは削除せずに残しておきたい。
したがって、こういう使い方はあまりしない気がする。

どちらかと言えば、読み込みオープンの時に指定できれば、中間ファイルを読んで、読み終わったら削除できるので便利な気がする。
しかし、このオペレーションはサポートされていない。
Files.newBufferedReaderではOpenOptionが指定できないし、
Files.newInputSreamでOpenOptionを指定しても下記の通り例外が発生する。

1
java.lang.UnsupportedOperationException: 'DELETE_ON_CLOSE' not allowed

ちなみに、この自動削除の仕組みを利用してプログラムを作成するのであれば、
中間ファイル名は自動でjavaに決めてもらった方が簡単な気がする。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package test.java;

import java.io.BufferedWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class DeleteOnCloseTest {
    public static void main(String[] args) {
        Path file3 = null;
        try {
            file3 = Files.createTempFile(null, null);
        } catch (Exception e) {
            System.out.println("create temp file failed. " + e.toString());
            throw new RuntimeException(e);
        }
        System.out.println("work file name:" + file3.toString());
        try (BufferedWriter writer = Files.newBufferedWriter(file3, StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE, StandardOpenOption.DELETE_ON_CLOSE)) {
            writer.write("test3-1\n");
            writer.write("test3-2\n");
            writer.write("test3-3\n");
            System.out.println("1.file3 exists? " + Files.exists(file3));
        } catch (Exception e) {
            System.out.println("write file failed. " + e.toString());
        }
        System.out.println("2.file3 exists? " + Files.exists(file3));
    }
}

実行すると、下記の通り出力される。

work file name:C:\Users\xxxxx\AppData\Local\Temp\6712414364776398896.tmp
1.file3 exists? true
2.file3 exists? false

Files.createTempFileで一時ファイルを作成できる。第一引数は中間ファイルのプレフィックス。第二引数は中間ファイルのサフィックス。
どうせ、見えずに消えてしまうファイルなので、どちらの引数もnullを指定している。
これで、ファイル名の重複を気にせず、中間ファイルを作成できる。

また、これらの処理がバッチ処理で、javaのプロセスが適宜終了するのであれば、下記の通りファイルに削除設定をしておいても、プロセス終了時に中間ファイルが削除される。

1
2
Path file = Paths.get("workfile");
file.toFile().deleteOnExit();
同じタグの記事
同じカテゴリの記事

macOSをHigh Sierraに更新した。スクリプト実行環境のバージョンは変わったのか

macOSをHigh Sierraに更新した。スクリプト実行環境のバージョンは変わったのか

macOSをHigh Sierraに更新した。スクリプト実行環境のバージョンは変わったのか。

python

High Sierra

1
2
3
Python 2.7.10 (default, Jul 15 2017, 17:16:57)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

Sierra

1
2
3
Python 2.7.10 (default, Feb  7 2017, 00:08:15)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

pythonバージョンは変更なし。
コンパイラのバージョンは上がっている。

perl

High Sierra

1
2
3
4
This is perl 5, version 18, subversion 2 (v5.18.2) built for darwin-thread-multi-2level
(with 2 registered patches, see perl -V for more detail)

Copyright 1987-2013, Larry Wall

Sierra

1
2
3
4
This is perl 5, version 18, subversion 2 (v5.18.2) built for darwin-thread-multi-2level
(with 2 registered patches, see perl -V for more detail)

Copyright 1987-2013, Larry Wall

perlのバージョンも変更なし。

PHP

High Sierra

1
2
3
PHP 7.1.7 (cli) (built: Jul 15 2017 18:08:09) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies

Sierra

1
2
3
PHP 5.6.30 (cli) (built: Feb  7 2017 16:18:37)
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies

なんと、 PHP5からPHP7へ。

sqlite3

High Sierra

1
SQLite version 3.19.3 2017-06-27 16:48:08

Sierra

1
SQLite version 3.16.0 2016-11-04 19:09:39

sqlite3はバージョンが上がっているけど、これがどこにどう影響するかがわからない。

まとめ

バージョンが上がっているスクリプト言語もあった。
本当はPythonが3系列になっていると嬉しかった。

同じタグの記事
同じカテゴリの記事

オレオレ証明書サーバに接続

オレオレ証明書サーバに接続する

java(7~)でオレオレ証明書のサーバにssl接続すると、証明書エラーが発生する。
こういう場合は、そのオレオレ証明書をJavaのキーストアに登録して、そのキーストアでサーバを認証してアクセスする。なお、それように新しいキーストアを複数作成することができる。

キーストアを作成する

任意のcrtファイルを取得し、そのcrtファイルを含むキーストアを作成する。

1
keytool -importcert -file サーバ証明書.crt -keysore my.ks -alias 任意の証明書につける名前  -storepass changeit

-storepassを指定しなかった場合は、プロンプトで確認される。
なお、デフォルトのキーストアのパスワードはchangeitらしい。

作成したキーストアファイルを利用してjavaプロセスを起動する。

1
java -Djavax.net.ssl.trustStore=C:\my.ks -Djava.net.ssl.trustStorePassword=changeit my.java.Program

これで、任意の証明書を信頼済みとしてhttpsのサーバにアクセスできる。
なお、サーバ名と証明書のサーバ名が不一致の場合は、流石に補正できないので、そこはサーバ側で合わせておく。
クライアントのhostsで補正しても良い。

同じタグの記事
同じカテゴリの記事

ApacheのVirtualHost機能を使う

ApacheのVirtualHost機能を使う

localhostのwordpressに仮想のホスト名でアクセスする。
localhost:80の仮想パスにwordpressを表示しても良かったが、運用されているwordpressは仮想パスでは運用していなかったので、環境を合わせるためVirtualHost機能を利用して、仮想パスなしでwordpressが表示されるように設定した。

下記の設定をhttpd.confやhttpd.confでインクルードされる設定ファイルに書き込む。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<VirtualHost *:80>
    ServerAdmin test@test.com
    DocumentRoot "D:/wordpress"
    ServerName wordpress
    ErrorLog "logs/wordpress-error.log"
    CustomLog "logs/wordpress-access.log" common
</VirtualHost>

<Directory "D:/wordpress">
    Options Indexes FollowSymLinks ExecCGI
    AllowOverride all
    #Order allow,deny
    #Allow from all
    Require all granted
</Directory>

ログもVirtualHostごとに設定できる。
ディレクトリのアクセス権の設定は、Apapche2.2ではOrder,Allowで設定し、Apache2.4ではRequireで設定する。

この設定の後Apacheを再起動し、hostsにwordpressという名前でApacheサーバを参照できるようにし、
http://wordpress/にアクセスする。

同じタグの記事
  • ありません。
同じカテゴリの記事

lsでファイルのみリストする

lsでファイルのみをリストする

データディレクトリにはディレクトリとファイルが混ざって格納されている。その中でもファイルのみをlsでリスト表示したい時は下記の通りコマンドを叩く。

1
ls -lF | grep -v /

ls -Fでディレクトリに/が付与されるようになる。それをgrepで除外する。

例えば、/bin配下のsuidが設定されたファイルのリストを見るには

1
ls -lF | grep -v / | grep rws

ファイル名とディレクトリ名が似通っていたり、grepのキーワードがどちらにも掛かってしまう場合はこの方法が有効。

同じタグの記事
同じカテゴリの記事

Windowsバッチファイルの処理結果判定

Windowsバッチファイルの処理結果判定

ツール的なバッチ処理は、WindowsでもLinuxでも動くように、通常はperlやpythonでスクリプトを作ってしまう。
しかし、本当に単純にプログラムを順番通りに実行させるだけなら、Windowsバッチを作成することがある。
この時、呼び出すコマンドが正常終了した場合は、次のコマンドを実行してほしいが、失敗したのであれば、そこで終了してほしい。

ERRORLEVELを判定する

呼び出したコマンドが正常終了したかどうかは、ERRORLEVELを判定すれば判断できる。
ほとんどのコマンドはERRORLEVELが0の場合、正常終了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@ECHO OFF

command1

IF ERRORLEVEL 1 GOTO ERR

command2

IF ERRORLEVEL 1 GOTO ERR

EXIT

:ERR
  PAUSE

command1,command2が異常終了時に終了コードとして1以上の値を返す時、ERRというラベルにジャンプする。
IF ERRORLEVEL 1 GOTO は、終了コードが1以上のときに、GOTO先のラベルにジャンプする。

結果コードにマイナス値が存在し、0のみが正常値であるならば、下記のように記述する必要がある。

1
IF NOT "%ERRORLEVEL%" == "0" GOTO ERR
1
IF %ERRORLEVEL% NEQ 0 GOTO ERR

Windowsのバッチファイルは比較構文が色々準備されているようだ。

同じタグの記事
  • ありません。
同じカテゴリの記事

Microsoft Arc Keyboard

Microsoft Arc Keyboard

アーチ型の弧を描くキーボード。
Arc名はそこからきているのだろう。
この形状が手首への負担を減らすと思い買ってみた。
実際、手首の負担は小さくなったと感じる。
キータッチの軽さもあって、快適なキータイプができるようになった。
ボディサイズも小さめで、机の上が広く使える。
しかし、bluetoothには対応していない。

良いところ

* 手首の疲れを軽減させる弧を描く形状
* 小さくて軽量なボディ


残念なところ

* blutoothには対応していない


まとめ

あまり不満なところのないキーボードであった。
かといって、良いところもそれほどなく、現在は手放してしまった。

同じタグの記事
同じカテゴリの記事

javascriptの配列操作

javascriptの配列操作

javascriptの配列操作は、すぐ忘れる。

準備

1
var arr = [];

追加

1
2
arr.push('a');
arr.push('b');

arr は [‘a’, ‘b’] になる。pushは末尾に追加される。
先頭に追加するには

1
2
arr.unshift('x');
arr.unshift('y');

arr は [‘y’, ‘x’, ‘a’, ‘b’] になる。
unshiftの命名はperlからだろうか。

配列数の取得

1
var l = arr.length;

l は 4になる。

除去

1
2
var i = arr.pop();
var j = arr.pop();

arr は [‘y’, ‘x’] になる。
i は ‘b’、j は ‘a’ になる。
末尾から除去されてている。スタックとして使う時に便利。

先頭から取得する場合、キューとして使う場合は、

1
2
var m = arr.shift();
var n = arr.shift();

arr は [] になる。
m は ‘y’、n は ‘x’になる。

同じタグの記事
同じカテゴリの記事